Skip to content
Go back

バックテストとフォワードテストの重要性

Updated:
Edit page

バックテストとフォワードテストの重要性

FXでも株式市場でも同じですが、テクニカル分析+システムトレードの大きな強みは、
「過去データを用いて、自分が考えたテクニカル指標や戦略が期待通りの成績を残せるかを事前に評価できること」にあります。

もちろん、いきなり本番取引で利益を出したいという気持ちも理解できます。
ですが、最終的に果実を得るためには 戦略を事前にしっかり評価するプロセス が何より重要です。
最近では「システムトレードは利益を出すことだけでなく、そのプロセス自体を楽しめるかどうかが大切なのではないか」と感じています。


バックテストとは?

バックテストとは、過去の株価や為替データに対して、自分が作った売買ルールやアルゴリズムを適用し、
「その戦略が実際にどのような成績を出していたか」を検証する作業です。

バックテストの目的

勝率やドローダウンをどの指標で見るべきかは システムトレードの評価基準一覧 で整理しています。

実際のやり方例

  1. 過去数年分の株価や為替データを用意する
  2. 自分の売買ルール(例:移動平均線のゴールデンクロスで買い、デッドクロスで売り)を適用
  3. 各トレードの損益を集計し、勝率やリターンを算出
  4. グラフ化して損益曲線を確認

フォワードテストとは?

フォワードテストとは、バックテストで有効と判断された戦略を、
今度は 実際の相場の最新データ を使って検証する作業です。

いきなり実資金で取引するのではなく、デモ口座や少額のリアル資金を使ってテストします。

フォワードテストの目的

実際のやり方例

  1. バックテストで有望だった戦略を選ぶ
  2. デモ口座で1〜3か月ほど運用し、実際の成績を記録する
  3. バックテストとの乖離がないか確認
  4. 問題がなければ少額資金で本番運用を開始

プログラムが苦手な人でもできること

ここまでシステムトレード前提で話しましたが、
プログラムに明るくない方でも Excelなどを使えば過去データを基にシミュレーション できます。

過去データは証券会社や公開API、無料サイトなどから入手しやすいため、
「もしこのルールで取引していたらどうなっていたか」を手軽に検証することが可能です。


Python によるバックテストのコード例

実際にどのようにバックテストを実装するのか、ゴールデンクロス / デッドクロス を使った最小構成のサンプルを示します。pandas と yfinance だけで動く最小例です。

import pandas as pd
import yfinance as yf

# 過去5年分の日足を取得(例: トヨタ 7203.T)
df = yf.download("7203.T", period="5y", interval="1d")
df = df.rename(columns=str.lower).dropna()

# 短期/長期の移動平均
df["sma_short"] = df["close"].rolling(25).mean()
df["sma_long"]  = df["close"].rolling(75).mean()

# シグナル: 短期 > 長期 なら買い(1), そうでなければ現金(0)
df["signal"] = (df["sma_short"] > df["sma_long"]).astype(int)

# 翌日の始値で約定する前提にするため shift(1)
df["ret"]      = df["close"].pct_change()
df["strategy"] = df["signal"].shift(1) * df["ret"]

# 累積リターン
df[["ret", "strategy"]].add(1).cumprod().plot()

# 成績サマリー
total_return = df["strategy"].add(1).prod() - 1
win_rate     = (df["strategy"] > 0).mean()
max_dd       = (df["strategy"].add(1).cumprod() /
                df["strategy"].add(1).cumprod().cummax() - 1).min()

print(f"総リターン: {total_return:.2%}")
print(f"勝率      : {win_rate:.2%}")
print(f"最大DD    : {max_dd:.2%}")

このコードの骨格さえ押さえておけば、あとは signal の作り方を差し替えるだけで、ボリンジャーバンドでも RSI でも同じ枠組みで検証できます。

最初にハマった落とし穴


カーブフィッティングを避ける

バックテストで一番の敵は カーブフィッティング(過剰最適化) です。パラメータをいじり倒して過去データに合わせ込むと、本番ではまったく通用しないモデルが出来上がります。

私が実際に踏んでしまった典型的なパターンはこの3つでした。

  1. 全期間で最適化して最高成績を採用: 直近相場に特化した設定になり、別の相場環境では逆ポジを取るようになる
  2. パラメータを 10 個以上同時に最適化: 組み合わせ爆発で偶然良いだけの設定が必ず出てくる
  3. サンプル数が少ない銘柄・期間での検証: 数十トレードしか発生せず、勝率の信頼区間が広すぎる

これを避けるには、次のルールで運用するのが効きました。

戦略が「偶然の当たり」ではないかを統計的に判定する手順は 戦略の有効性を統計的に評価する にまとめました。


ウォークフォワード分析

カーブフィッティング対策の決定版が ウォークフォワード分析 です。過去データを時間方向にスライドさせながら、「最適化区間」と「検証区間」を交互に動かしていきます。

|---- 最適化 1 ----|-- 検証 1 --|
         |---- 最適化 2 ----|-- 検証 2 --|
                  |---- 最適化 3 ----|-- 検証 3 --|

各「検証 N」で得られた成績だけを連結したものが、そのロジックの 擬似的な実運用成績 になります。Python で最小構成を書くと次のようになります。

import numpy as np

def walk_forward(df, train_len=252 * 2, test_len=252, param_grid=range(5, 60, 5)):
    results = []
    start = 0
    while start + train_len + test_len <= len(df):
        train = df.iloc[start : start + train_len]
        test  = df.iloc[start + train_len : start + train_len + test_len]

        # 最適パラメータ探索(例: 短期SMAの期間)
        best_param, best_score = None, -np.inf
        for p in param_grid:
            score = evaluate(train, sma_short=p)   # 自作の評価関数
            if score > best_score:
                best_score, best_param = score, p

        # 決まったパラメータで検証区間を走らせる
        results.append(evaluate(test, sma_short=best_param))
        start += test_len   # 区間を1年分スライド

    return np.array(results)

この結果が一貫してプラスに推移していれば、パラメータの選び方自体が一定の汎化性能を持つと言えます。逆に大きなブレが残るようなら、そのロジックはまだ本番運用に耐えません。

ウォークフォワードはパラメータ組み合わせが爆発しやすいため、実行時間が課題になります。その高速化の実例は バックテストを並列化して7日→1日に短縮した話 で紹介しています。


まとめ

システムトレードは「利益を出すこと」だけでなく、
戦略を組み立て、検証し、改善していくプロセス自体を楽しむこと が継続の秘訣だと思います。


Edit page
Share this post on:

Related Posts


Next Post
システムトレードの評価基準一覧