FX / EA Python

FXの移動平均線の最適な期間設定は?5通貨で100通りのSMAを検証

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

FXで移動平均線を使うとき、

  • SMAは何期間に設定すればいいの?
  • 5・20・75・200など、結局どの期間が使いやすいの?

と悩む方も多いのではないでしょうか。

移動平均線にはよく使われる期間がありますが、実際にどの設定が機能するかは、通貨や売買方向によって異なります。

そこで今回は、USDJPY・EURJPY・GBPJPY・EURUSD・AUDUSDの5通貨を対象に、短期SMA10種類×長期SMA10種類の100通りをLONG・SHORTそれぞれで検証しました。

単純な合計利益だけではなく、PF・最大DD・直近6か月の成績・周辺設定の安定性なども含めて評価し、成績が良かったSMAの期間設定を紹介します。

「移動平均線の期間をどう設定すればいいか迷っている」という方は、ぜひ参考にしてください。

FXの移動平均線(MA)とは

移動平均線(MA:Moving Average)とは、一定期間の価格の平均値を線で表したテクニカル指標です。

今回の検証では、基本的な単純移動平均線(SMA)を使用します。

SMAは期間が短いほど価格へ素早く反応し、期間が長いほど値動きが滑らかになります。

本記事では、短期SMAと長期SMAの組み合わせを変えながら、どの期間設定が成績につながりやすいのかを検証します。

ゴールデンクロスとデッドクロス

2本の移動平均線を使った代表的な売買シグナルが、ゴールデンクロスとデッドクロスです。

  • ゴールデンクロス:短期SMAが長期SMAを下から上へ抜ける
  • デッドクロス:短期SMAが長期SMAを上から下へ抜ける

今回の検証では、次の条件でLONG・SHORTを判定します。

方向エントリー決済
LONGゴールデンクロスデッドクロス
SHORTデッドクロスゴールデンクロス

クロスが確定した次のローソク足の始値で売買する条件としています。

移動平均線でよく使われる期間

移動平均線には「この期間を使えば必ず勝てる」という決まった設定はありません。

取引する時間足やトレードスタイルによって、使用される期間は異なります。

代表的な設定としては、短期では5・10・20・25、中期では50・75、長期では100・200などが使われることがあります。

ただし、実際にどの期間が有効なのかは、通貨や相場環境によって変わります。

そこで今回の検証では、一般的に使われる期間だけに限定せず、比較範囲を広げるために以下のSMAを使用します。

  • 短期SMA:5、10、15、20、25、30、35、40、45、50
  • 長期SMA:60、75、100、120、150、200、250、300、400、500

短期10種類×長期10種類で、1通貨・1方向につき100通りのSMA設定を比較します。

今回の移動平均線の検証条件

今回は、5通貨の15分足データを使用して、短期SMAと長期SMAの組み合わせを検証します。

1通貨・1方向につき100通りのSMA設定を比較し、LONG・SHORTをそれぞれ検証します。

今回検証する通貨・期間・SMA

今回の主な検証条件は以下のとおりです。

項目検証条件
通貨USDJPY・EURJPY・GBPJPY・EURUSD・AUDUSD
時間足15分足
移動平均線SMA(単純移動平均線)
短期SMA5・10・15・20・25・30・35・40・45・50
長期SMA60・75・100・120・150・200・250・300・400・500
組み合わせ1通貨・1方向につき100通り
検証期間2024年1月1日 ~ 2026年7月31日
価格データGMO FX APIで取得したローソク足
使用価格ASK

今回は移動平均線そのものの成績を確認するため、1時間足・4時間足・日足などの上位足フィルターは使用しません。

データはGMOのAPIを利用し、取得しています。

実際に取得をしてみたい方は、以下の記事を参考にしてみてください。

LONG・SHORTの検証条件

LONGとSHORTはそれぞれ独立して検証します。

項目LONGSHORT
エントリーゴールデンクロスデッドクロス
約定クロス確定後の次足始値クロス確定後の次足始値
決済デッドクロス後の次足始値ゴールデンクロス後の次足始値
TP・SL使用しない使用しない
同時保有最大1ポジション最大1ポジション
データ終了時最終足終値で決済最終足終値で決済

今回はSMAの期間設定による違いを比較するため、固定のTP・SLは使用せず、反対方向のクロスが発生するまでポジションを保有する条件としています

なお、今回の検証ではASK価格のみを使用しており、以下の取引コストは考慮していません

  • スプレッド
  • スリッページ
  • 手数料
  • スワップ

そのため、平均利益が小さい設定については、実際の取引では成績が悪化する可能性があります。

成績が良かったSMA設定の選定基準

今回検証した100通りのSMA設定から、成績が良かった組み合わせを選ぶため、単純に合計pipsが大きい順では順位を決めていません。

全期間だけで好成績だった設定や、特定の期間だけ偶然良かった設定をできるだけ避けるため、まず以下の条件で候補を絞り込みます。

足切り条件

  • 全検証期間の合計pipsがプラス
  • 全検証期間のPFが1.05以上
  • 全検証期間の取引回数が100回以上
  • 直近6か月の合計pipsがプラス
  • 直近6か月のうち3か月以上がプラス

条件を通過した設定について、以下の7項目を100点満点で評価します。

評価項目配点
全検証期間の合計pips20点
PF15点
利益 ÷ 最大DD20点
平均pips10点
周辺パラメータの安定性15点
直近6か月の成績15点
取引回数5点
合計100点

特に重視するのが、利益に対する最大DDと周辺パラメータの安定性です。

例えば、短期SMA45・長期SMA60だけが好成績でも、その周辺設定がすべてマイナスであれば、偶然その組み合わせだけが良かった可能性があります。

そこで、短期・長期SMAの一覧上で隣接する設定を確認し「条件を満たした周辺設定数 ÷ 存在する周辺設定数」から周辺パラメータの安定性を評価します。

最終的に、評価点が高かった設定を最大5つまで掲載します。

足切り条件を満たす設定が3つならTOP3、候補がなければ「今回の条件では有効な設定なし」として結果を掲載します。

また、今回の検証に使用したPythonスクリプトも掲載しておきます

短期SMA10種類×長期SMA10種類の100通りについて、5通貨のLONG・SHORTをそれぞれ検証できるようにしています。

気になる方は、是非以下のコードをコピーして使ってみてください。

run_ma_backtest.py
"""15分足のSMAクロスを100パターン一括検証する。

15分足のシグナルが確定した次の足の始値で約定し、反対クロスで決済する。
標準ライブラリだけで実行できる。
"""

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

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

# 検証する通貨ペアです。
SYMBOLS = ("USD_JPY", "EUR_JPY", "GBP_JPY", "EUR_USD", "AUD_USD")

# 売買の判断には15分足だけを使います。
ENTRY_TIMEFRAME = "15min"
ENTRY_TIMEFRAME_DURATION = timedelta(minutes=15)
PRICE_TYPE = "ASK"

# 15分足で検証する短期SMAと長期SMAです。
# すべての短期期間がすべての長期期間より短いため、10 × 10 = 100通りになります。
DEFAULT_FAST_PERIODS = (5, 10, 15, 20, 25, 30, 35, 40, 45, 50)
DEFAULT_SLOW_PERIODS = (60, 75, 100, 120, 150, 200, 250, 300, 400, 500)

# 入力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 = (
    "通貨ペア",
    "短期SMA期間",
    "長期SMA期間",
    "取引回数",
    "勝ち取引数",
    "負け取引数",
    "勝率(%)",
    "合計損益(pips)",
    "平均損益(pips)",
    "プロフィットファクター",
    "最大ドローダウン(pips)",
)

# 1回ごとの取引履歴に保存する列です。
TRADE_COLUMNS = (
    "取引番号",
    "エントリー日時",
    "エントリー価格",
    "決済日時",
    "決済価格",
    "決済理由",
    "保有時間(分)",
    "損益(pips)",
)


def load_candles(path):
    """fetch_gmo_candles.pyが作るOHLC CSVを読み込む。"""
    candles = []

    # 始値と終値をバックテストで使いやすい数値に変換します。
    # 高値と安値は今回のクロス決済では使わないため読み込みません。
    with path.open("r", encoding="utf-8-sig", newline="") as file:
        for row in csv.DictReader(file):
            timestamp = datetime.fromisoformat(row["timestamp_jst"])
            candles.append(
                {
                    "open_time": timestamp,
                    "close_time": timestamp + ENTRY_TIMEFRAME_DURATION,
                    "open": float(row["open"]),
                    "close": float(row["close"]),
                }
            )

    return candles


def calculate_sma(closes, period):
    """終値の単純移動平均を未来参照なしで計算する。"""
    # 例:終値が [100, 101, 102, 103]、期間が3の場合、
    # SMAは [None, None, 101, 102] になります。
    # 101 = (100 + 101 + 102) ÷ 3、102 = (101 + 102 + 103) ÷ 3です。
    # SMAをまだ計算できない最初の期間にはNoneを入れます。
    values = [None] * len(closes)
    window_sum = 0.0

    for index, close in enumerate(closes):
        # 新しい終値を合計へ追加し、期間より古い終値を合計から外します。
        # 上のSMA3の4本目なら、103を加えた後、期間外になった100を引きます。
        window_sum += close
        if index >= period:
            window_sum -= closes[index - period]

        # 必要な本数が揃った位置からSMAを記録します。
        # SMA3なら、3本目にあたるindex=2から計算できます。
        if index >= period - 1:
            values[index] = window_sum / period
    return values


def crossover(previous_fast, previous_slow, fast, slow):
    """ゴールデンクロスなら1、デッドクロスなら-1、それ以外は0。"""
    # 短期SMAが長期SMAを下から上へ抜けた場合です。
    # 例:前の足が「短期99、長期100」、現在足が「短期101、長期100」なら、
    # 短期SMAが下側から上側へ移ったため、ゴールデンクロスとして1を返します。
    if previous_fast <= previous_slow and fast > slow:
        return 1

    # 短期SMAが長期SMAを上から下へ抜けた場合です。
    # 例:前の足が「短期101、長期100」、現在足が「短期99、長期100」なら、
    # 短期SMAが上側から下側へ移ったため、デッドクロスとして-1を返します。
    if previous_fast >= previous_slow and fast < slow:
        return -1
    return 0


def collect_trades(
    candles,
    fast_sma,
    slow_sma,
    side,
    pip_size,
    evaluation_start_index,
):
    """片側戦略を検証し、1回ごとの取引履歴を返す。"""
    # LONGはゴールデンクロスで入り、デッドクロスで出ます。
    # SHORTはその反対です。
    entry_signal = 1 if side == "LONG" else -1
    exit_signal = -entry_signal

    # entry_priceがNoneならポジションを持っていない状態です。
    # pendingは、前の足で決まった注文を次の足の始値で実行するための印です。
    entry_price = None
    entry_time = None
    entry_index = None
    pending_entry = False
    pending_exit = False
    trades = []

    # 全設定を公平に比べるため、最長のSMA500が計算できる位置から始めます。
    for index in range(max(1, evaluation_start_index), len(candles)):
        # 前の15分足で決済が決まっていれば、現在足の始値で決済します。
        if entry_price is not None and pending_exit:
            direction = 1.0 if side == "LONG" else -1.0
            exit_price = candles[index]["open"]

            # USDJPYのLONGを150.00円で買い、150.20円で決済した例では、
            # (150.20 - 150.00) ÷ 0.01 = 20pipsです。
            # SHORTはdirectionが-1になるため、値下がりしたときに利益がプラスになります。
            trades.append(
                {
                    "取引番号": len(trades) + 1,
                    "エントリー日時": entry_time.isoformat(),
                    "エントリー価格": round(entry_price, 6),
                    "決済日時": candles[index]["open_time"].isoformat(),
                    "決済価格": round(exit_price, 6),
                    "決済理由": "反対クロス",
                    "保有時間(分)": (index - entry_index) * 15,
                    "損益(pips)": round(
                        direction * (exit_price - entry_price) / pip_size, 2
                    ),
                }
            )
            entry_price = None
            entry_time = None
            entry_index = None
        pending_exit = False

        # 前の15分足でエントリーが決まっていれば、現在足の始値で約定します。
        # 例:10:00開始の足が10:15に確定してクロスした場合、
        # クロスした足の終値ではなく、10:15開始足の始値で約定します。
        if entry_price is None and pending_entry:
            entry_price = candles[index]["open"]
            entry_time = candles[index]["open_time"]
            entry_index = index
        pending_entry = False

        # 直前と現在の短期SMA・長期SMAを比べてクロスを判定します。
        previous_fast = fast_sma[index - 1]
        previous_slow = slow_sma[index - 1]
        current_fast = fast_sma[index]
        current_slow = slow_sma[index]
        if None in (previous_fast, previous_slow, current_fast, current_slow):
            continue
        signal = crossover(
            float(previous_fast), float(previous_slow), float(current_fast), float(current_slow)
        )

        # ポジションがない場合は、15分足でクロスした次の足でエントリーします。
        # ここではまだ約定せず、pending_entryをTrueにして次のループまで待ちます。
        if entry_price is None and signal == entry_signal:
            pending_entry = True

        # ポジション保有中に反対クロスが出たら、次の15分足で決済します。
        elif entry_price is not None and signal == exit_signal:
            pending_exit = True

    # データの最後までポジションが残った場合は、最終終値で決済します。
    if entry_price is not None:
        direction = 1.0 if side == "LONG" else -1.0
        exit_price = candles[-1]["close"]
        trades.append(
            {
                "取引番号": len(trades) + 1,
                "エントリー日時": entry_time.isoformat(),
                "エントリー価格": round(entry_price, 6),
                "決済日時": candles[-1]["close_time"].isoformat(),
                "決済価格": round(exit_price, 6),
                "決済理由": "データ終了",
                "保有時間(分)": (len(candles) - entry_index) * 15,
                "損益(pips)": round(
                    direction * (exit_price - entry_price) / pip_size, 2
                ),
            }
        )
    return trades


def summarize(symbol, fast_period, slow_period, trades):
    """取引ごとのpips損益から、成績をまとめる。"""
    profits = [trade["損益(pips)"] for trade in trades]
    # プラスなら勝ち、マイナスなら負けとして分けます。
    wins = [profit for profit in profits if profit > 0]
    losses = [profit for profit in profits if profit < 0]
    gross_profit = sum(wins)
    gross_loss = abs(sum(losses))

    # プロフィットファクターは「総利益 ÷ 総損失」です。
    # 例:勝ち取引の合計が300pips、負け取引の合計が-200pipsなら、
    # プロフィットファクターは 300 ÷ 200 = 1.5 です。
    profit_factor = gross_profit / gross_loss if gross_loss else None

    # 取引順に累積損益を作り、過去の最高値からの最大下落幅を求めます。
    # 例:累積損益が [100, 250, 180, 50] と推移した場合、
    # 過去最高の250から50まで200pips下落するため、最大DDは200pipsです。
    equity = peak = max_drawdown = 0.0
    for profit in profits:
        equity += profit
        peak = max(peak, equity)
        max_drawdown = max(max_drawdown, peak - equity)

    trade_count = len(profits)
    return {
        "通貨ペア": symbol,
        "短期SMA期間": fast_period,
        "長期SMA期間": slow_period,
        "取引回数": trade_count,
        "勝ち取引数": len(wins),
        "負け取引数": len(losses),
        "勝率(%)": round(len(wins) / trade_count * 100, 2) if trade_count else 0.0,
        "合計損益(pips)": round(sum(profits), 2),
        "平均損益(pips)": round(sum(profits) / trade_count, 2) if trade_count else 0.0,
        "プロフィットファクター": round(profit_factor, 4) if profit_factor is not None else "",
        "最大ドローダウン(pips)": round(max_drawdown, 2),
    }


def run_grid(candles, symbol, fast_periods, slow_periods, output_dir):
    """1通貨について、15分足のMA100パターンを検証して順次保存する。"""
    # 15分足の終値から、検証で必要なすべてのSMAを先に計算します。
    all_periods = set(fast_periods) | set(slow_periods)
    closes = [candle["close"] for candle in candles]
    entry_sma_by_period = {
        period: calculate_sma(closes, period) for period in all_periods
    }
    # 全組み合わせを最長MAが計算可能になった同じ時点から比較します。
    evaluation_start_index = max(all_periods)

    # 円を含む通貨ペアは1pips=0.01円、それ以外は1pips=0.0001です。
    pip_size = 0.01 if symbol.endswith("_JPY") else 0.0001

    # LONGとSHORTを分け、result.csvを検証中に1回だけ開きます。
    for side in ("LONG", "SHORT"):
        result_path = output_dir / symbol / side / "result.csv"
        result_path.parent.mkdir(parents=True, exist_ok=True)

        # "w"で開くため、再実行時は以前のresult.csvを上書きします。
        with result_path.open("w", encoding="utf-8-sig", newline="") as file:
            writer = csv.DictWriter(file, fieldnames=RESULT_COLUMNS)
            writer.writeheader()

            # 短期10種類 × 長期10種類を順番に検証します。
            for fast_period in fast_periods:
                for slow_period in slow_periods:
                    if fast_period >= slow_period:
                        continue

                    trades = collect_trades(
                        candles,
                        entry_sma_by_period[fast_period],
                        entry_sma_by_period[slow_period],
                        side,
                        pip_size,
                        evaluation_start_index,
                    )

                    # 1設定の検証が終わった時点で取引履歴を保存します。
                    # 次の設定へ進む前にtradesが置き換わるため、全設定分を保持しません。
                    trade_path = (
                        output_dir
                        / symbol
                        / side
                        / "trades"
                        / f"SMA_{fast_period}_{slow_period}.csv"
                    )
                    write_csv(trade_path, trades, TRADE_COLUMNS)

                    # 取引履歴を保存した直後に、この設定の検証結果を1行追加します。
                    writer.writerow(
                        summarize(symbol, fast_period, slow_period, trades)
                    )


def write_csv(path, rows, columns):
    """検証結果をCSVへ保存する。"""
    # 保存先フォルダがなければ作成します。
    path.parent.mkdir(parents=True, exist_ok=True)

    # Excelでも文字化けしにくいUTF-8 BOM付きで保存します。
    with path.open("w", encoding="utf-8-sig", newline="") as file:
        writer = csv.DictWriter(file, fieldnames=columns)
        writer.writeheader()
        writer.writerows(rows)


def main():
    """CSVの読み込みから検証結果の保存までを実行する。"""
    # スクリプト全体の実行時間を測り始めます。
    start_time = time.perf_counter()

    # 短期MAが長期MAより短い、有効な組み合わせ数を確認します。
    combinations = sum(
        fast < slow for fast in DEFAULT_FAST_PERIODS for slow in DEFAULT_SLOW_PERIODS
    )
    if combinations == 0:
        raise SystemExit("短期期間 < 長期期間となる組み合わせがありません")

    print(f"有効なMA組み合わせ: {combinations}パターン")

    # 5通貨を1つずつ検証します。
    for symbol in SYMBOLS:
        # この通貨の15分足CSVを読み込みます。
        path = DEFAULT_DATA_DIR / symbol / f"{ENTRY_TIMEFRAME}_{PRICE_TYPE}.csv"
        candles = load_candles(path)

        # 100パターンを実行し、全結果を保存します。
        print(f"[RUN] {symbol}: 15min")
        run_grid(
            candles,
            symbol,
            DEFAULT_FAST_PERIODS,
            DEFAULT_SLOW_PERIODS,
            DEFAULT_OUTPUT_DIR,
        )

    elapsed_seconds = time.perf_counter() - start_time
    print(f"完了: {DEFAULT_OUTPUT_DIR}")
    print(f"実行時間: {elapsed_seconds:.2f}秒")
    return 0


# このファイルを直接実行したときだけmain()を動かします。
if __name__ == "__main__":
    raise SystemExit(main())

実行コマンドは以下です。

python run_ma_backtest.py

検証結果は通貨・LONG・SHORTごとにCSVへ保存されるため、次の章ではその結果をもとに成績が良かったSMA設定を紹介します。

通貨別に成績が良かった移動平均線

ここでは、足切り条件と100点評価を通過した設定を、通貨ごとに紹介します。

正式評価では、条件を満たした設定が5つ未満の場合は、無理にTOP5を作らず、通過した設定のみを掲載します。

今回の候補数は以下のとおりです。

通貨LONGSHORT
USDJPYTOP5TOP5
EURJPYTOP4候補なし
GBPJPYTOP4候補なし
EURUSD候補なし候補なし
AUDUSDTOP4候補なし

USDJPYの検証結果

LONG

USDJPYのLONGでは、81設定が足切り条件を通過しました。

その中で総合評価が高かったTOP5は以下のとおりです。

順位短期SMA長期SMA点数合計pipsPF利益/DD周辺安定率直近6か月の合計pips
13510091.50+2,348.11.32741.57100%+618.5
2457589.00+1,905.61.23551.50100%+576.3
3306086.60+2,249.71.25001.68100%+683.9
4506086.60+1,960.31.18771.71100%+440.4
53010086.00+2,082.31.27071.36100%+755.8

最も評価が高かったのは、短期SMA35・長期SMA100の組み合わせです。

合計pipsだけを見ると短期SMA30・長期SMA60の成績も非常に良好でしたが、PF・最大DD・直近6か月の成績・周辺設定の安定性まで含めて評価した結果、短期SMA35・長期SMA100が1位となりました。

また、TOP5すべてで周辺安定率が100%となっており、USDJPYのLONGは今回の5通貨の中でも比較的安定した結果です。

SHORT

USDJPYのSHORTでは、10設定が足切り条件を通過しました。

その中で総合評価が高かったTOP5は以下のとおりです。

順位短期SMA長期SMA点数合計pipsPF利益/DD周辺安定率直近6か月の合計pips
1306074.30+1,295.41.13051.22100%+24.4
23010071.30+1,107.31.14670.85100%+80.8
34010069.15+952.71.12820.7275%+224.8
4356067.10+916.81.09111.00100%+42.6
52010061.88+841.11.10040.7062.5%+281.0

SHORTで最も評価が高かったのは、短期SMA30・長期SMA60の組み合わせです。

SHORTでも有効候補は見つかりましたが、LONGと比べるとPFや利益/DDは低めです。

USDJPYではLONG・SHORTの両方で候補が見つかりましたが、総合的にはLONGの方が安定した結果となりました。

EURJPYの検証結果

LONG

EURJPYのLONGで足切り条件を通過したのは4設定です。

順位短期SMA長期SMA点数合計pipsPF利益/DD周辺安定率直近6か月の合計pips
1456064.80+1,143.91.10460.9440%+185.9
2540061.10+705.81.12780.5560%+63.4
3356055.70+942.01.08940.6220%+26.7
4406050.30+749.61.06930.5040%+71.7

最も評価が高かったのは、短期SMA45・長期SMA60の組み合わせです。

また、短期SMA35・40・45と長期SMA60の組み合わせが複数候補に残っており、長期SMA60付近に一定の傾向が確認できます。

ただし、利益/DDはUSDJPYのLONGより低く、安定性はやや劣る結果です。

SHORT

EURJPYのSHORTでは、正式な足切り条件を満たす設定はありませんでした。

直近の一部期間で利益が出た設定はありましたが、長期的な成績を含めて評価すると、優位性は確認できませんでした。

今回の条件では、EURJPYのSHORTに有効なSMA設定は見つかりませんでした

GBPJPYの検証結果

LONG

GBPJPYのLONGでは、4設定が足切り条件を通過しました。

順位短期SMA長期SMA点数合計pipsPF利益/DD周辺安定率直近6か月の合計pips
1456082.50+2,645.01.21682.2240%+841.4
2406073.80+1,970.51.16231.6720%+414.3
3506069.60+1,594.71.12041.1633.3%+486.6
4510052.00+1,076.51.09570.960%+18.0

最も評価が高かったのは、短期SMA45・長期SMA60の組み合わせです。

短期SMA40・45・50と長期SMA60の組み合わせがすべて候補に残っており、短期SMA40~50・長期SMA60付近に好成績が集中しています。

特に短期SMA45・長期SMA60は、利益・PF・最大DD・直近6か月の成績のバランスが良く、今回の有力候補です。

SHORT

GBPJPYのSHORTでは、正式な足切り条件を満たす設定はありませんでした。

全期間では小幅にプラスとなった設定もありましたが、PFなどの足切り条件を満たさなかったため候補から除外しています。

EURUSDの検証結果

LONG

EURUSDのLONGでは、正式な足切り条件を通過した設定はありませんでした。

全期間だけを見ると利益が出ていた設定はありましたが、直近6か月の成績まで確認すると候補が残りませんでした。

つまり、過去の全期間成績だけでは良く見えても、最近の相場では機能していない設定があったということです。

全期間の合計pipsだけで設定を選ばないことの重要性が分かる結果となりました。

SHORT

EURUSDのSHORTでも、正式な候補はありませんでした。

今回のMAクロス条件では、EURUSDはLONG・SHORTともに有効な設定を選出できない結果となりました。

AUDUSDの検証結果

LONG

AUDUSDのLONGでは、4設定が足切り条件を通過しました。

順位短期SMA長期SMA点数合計pipsPF利益/DD周辺安定率直近6か月の合計pips
1256057.30+267.11.06260.5140%+56.3
2506051.60+235.21.05130.500%+182.0
3207549.38+302.91.07960.4712.5%+156.3
4306044.90+219.71.05410.3920%+60.3

最も評価が高かったのは、短期SMA25・長期SMA60の組み合わせです。

ただし、4設定ともPFは1.05~1.08程度で、利益/DDや平均pipsも低めです。

形式上は候補として残っていますが、スプレッドやスリッページなどの取引コストを考慮すると、優位性がなくなる可能性があります。

そのため、AUDUSDのLONGは候補はあるものの、実運用では慎重に評価したい結果です。

SHORT

AUDUSDのSHORTでは、正式な足切り条件を満たす設定はありませんでした。

全期間では小幅に利益が出た設定もありましたが、PFなどの正式基準を満たさなかったため、有効候補とは判断しませんでした。

移動平均線の検証結果を考察

今回の検証では、5通貨すべてで同じSMA設定が有効になるわけではなく、通貨や売買方向によって成績に大きな差が出ることが分かりました。

特に目立ったのは、LONGの方がSHORTより候補が多かった点です。

USDJPYではLONG・SHORTの両方で有効候補が見つかりましたが、EURJPY・GBPJPY・AUDUSDではLONGのみ候補が残り、EURUSDではLONG・SHORTとも正式な条件を満たす設定がありませんでした。

また、LONGでは長期SMA60付近が好成績となるケースが複数確認できました。

  • EURJPY:短期SMA35~45・長期SMA60
  • GBPJPY:短期SMA40~50・長期SMA60
  • AUDUSD:短期SMA25~30・長期SMA60など

特にGBPJPYでは、短期SMA40・45・50と長期SMA60の組み合わせが複数候補に残っています。

このように、1つの設定だけが突出するよりも、近い期間設定でも好成績が続いている範囲の方が信頼しやすいと考えられます。

一方、全期間の合計pipsだけでは判断できないケースもありました。

EURUSD LONGでは全期間で利益が出ていた設定がありましたが、直近6か月の成績まで確認すると、正式な候補は0となりました。

そのため、移動平均線の設定を選ぶ際は、過去の合計利益だけではなく、PF・最大DD・周辺設定の安定性・直近の成績まで確認することが重要です。

今回の結果から、すべての通貨に共通する「最適なSMA期間」があるとは言えません。

ただし、USDJPYやGBPJPYのLONGのように複数の設定で安定した成績が確認できるケースもあり、通貨と売買方向を分けて検証することで、有力な期間設定を絞り込めることが分かりました。

移動平均線をFXで使う際の注意点

今回の検証結果を実際のトレードへ活用する際は、以下の点に注意が必要です。

  • 最適な期間は通貨や売買方向によって異なる
    今回の検証でも、同じSMA設定でも通貨やLONG・SHORTによって成績に大きな差がありました。
  • 過去に好成績でも将来の利益は保証されない
    全期間では利益が出ていても、直近6か月では成績が悪化した設定もありました。
  • 1つだけ突出した設定には注意する
    特定の期間だけが好成績の場合は、偶然その設定だけが良かった可能性があります。近いSMA設定でも成績が維持されているか確認することが重要です。
  • 実際の取引では成績が悪化する可能性がある
    今回はスプレッド・スリッページ・手数料・スワップを考慮していません。特に平均pipsが小さい設定は、実取引では利益が残らない可能性があります。

今回の結果は「この期間を使えば必ず勝てる」というものではなく、SMAクロスを使った場合に比較的成績が良かった期間設定を確認したものです。

まとめ

今回は、USDJPY・EURJPY・GBPJPY・EURUSD・AUDUSDの5通貨を対象に、短期SMA10種類×長期SMA10種類の100通りをLONG・SHORTそれぞれで検証しました。

その結果、すべての通貨・方向に共通する「最適なSMA期間」はありませんでした。

一方で、USDJPYやGBPJPYのLONGでは複数の設定が足切り条件を通過し、比較的安定した成績が確認できました。

特に、

  • USDJPY LONG:短期SMA35・長期SMA100
  • GBPJPY LONG:短期SMA45・長期SMA60
  • EURJPY LONG:短期SMA45・長期SMA60

などが有力候補となっています。

反対に、EURJPY・GBPJPY・AUDUSDのSHORTや、EURUSDのLONG・SHORTでは正式な候補を選出できませんでした。

今回の検証から、移動平均線の期間設定は合計pipsだけで決めるのではなく、PF・最大DD・周辺設定の安定性・直近の成績まで含めて評価することが重要だと分かりました。

移動平均線を使う際は、通貨やLONG・SHORTごとに検証し、自分の取引条件に合った期間を選ぶようにしましょう。

今後は、TP・SLの設定や上位足の方向を考慮した条件も追加し、今回のSMAクロス戦略から成績がどのように変わるのか検証していく予定です。

-FX / EA, Python