ストラテジーテスターで勝率80%、プロフィットファクター2.0という結果が出たEAを、
実際にデモ口座で動かしてみたら全然勝てなかった、という経験をしたことはないでしょうか。
これは設定ミスではなく、バックテストと実運用の間に、構造的に埋められない「ズレ」
が存在するために起こります。
この記事では、そのズレを生む4つの原因(ティックデータの粒度・
スプレッドの固定化・スリッページ・過剰最適化)を、
MQL4のコード例とともに解説します。
・ストラテジーテスターの3つのモデリング方式の違い
・バックテストが固定スプレッドで計算される仕組み
・スリッページがテスターでは再現されない理由
・実運用に近づけるための検証手順
なぜ「勝てるはずのEA」が実運用で勝てないのか
まず、このズレがなぜ起きるのかを確認します。
バックテストは過去データの再現にすぎない
ストラテジーテスターは、過去のヒストリカルデータを使って、
そのEAが過去に発注していたであろう結果を再計算しているだけです。
実際の市場で発生する変動要因の一部は再現されません。
既刊の『固定ロットvs複利ロット比較EA』との違い
既刊記事は、同じ条件のバックテスト同士を比較する設計でした。
本記事はその一歩手前、バックテストという土台自体が実運用とどう違うのかを扱います。
この記事で扱う4つのズレの原因
ティックデータの粒度・スプレッドの固定化・スリッページ・過剰最適化の4つを、
順番に見ていきます。
原因1 ティックデータの粒度
ストラテジーテスターのモデリング方式の違いを確認します。
ストラテジーテスターの3つのモデリング方式
| モデリング方式 | 判定の細かさ | 速度 |
|---|---|---|
| Open prices only | 1本のバーに1回だけ判定 | 最速 |
| Control points | 複数時間足を合成して近似 | 中間 |
| Every tick | ティックデータに基づき毎ティック判定 | 最も遅い |
Open prices onlyは1本のバーに1回しか判定しない
Open prices onlyでは、1本のローソク足の間に価格がどう動いても、
始値の時点でしか売買判定を行いません。
バーの途中で損切りに触れていても、検出されないことがあります。
Every tickでも完全な再現ではない
・Everytickは最も実運用に近いモデリング方式
・ただし古いヒストリカルデータはティック単位でなく
・1分足等から人工的にティックを生成している場合がある
原因2 スプレッドの固定化
バックテストのスプレッドの扱いを確認します。
バックテストは固定スプレッドで計算されることが多い
ストラテジーテスターの設定で指定したスプレッド(例: 10ポイント固定)で、
全期間の損益が計算されます。
実運用ではスプレッドが拡大するタイミングがある
経済指標発表の直前・直後や、早朝の市場参加者が少ない時間帯には、
実際のスプレッドが平常時の数倍に拡大することがあります。
スプレッド拡大を検知するフィルターのコード
double currentSpreadPoints = MarketInfo(_Symbol, MODE_SPREAD);
if(currentSpreadPoints > MaxAllowedSpreadPoints)
{
Comment("スプレッド拡大中のため新規エントリーを見送ります: ",
currentSpreadPoints, "ポイント");
return;
}
MODE_SPREADで現在のスプレッド(ポイント単位)を取得し、
上限を超えていたら新規エントリーを見送る設計にできます。
原因3 スリッページ(約定価格のズレ)
バックテストと実運用でのスリッページの扱いの違いを確認します。
バックテストにはスリッページという概念が働かない
・ストラテジーテスターはOrderSend()を指定価格で即約定させる
・OrderSend()のslippage引数(5番目の整数)はテスターでは無視される
・この引数は実運用時のみ有効な『許容ズレ幅』の指定
実運用ではslippage引数を超えるとエラー138になる
bool sent = OrderSend(_Symbol, OP_BUY, Lots, Ask, 3, sl, tp,
"Comment", MagicNumber, 0, clrBlue);
if(!sent)
{
int err = GetLastError();
if(err == 138)
Print("リクオート発生。Ask/Bidを再取得して再送信してください");
}
slippage引数(上記の「3」)は、
指定した価格からこの範囲を超えて価格が動いていたら発注を拒否する、
という実運用専用の安全装置です。
既刊GetLastErrorの記事との接続
エラーコード138(リクオート)の詳しい扱いは、
既刊のGetLastError解説記事も参照してください。
原因4 過剰最適化(カーブフィッティング)
パラメーターの最適化が引き起こすズレを確認します。
過去データに最もフィットするパラメーターを探しすぎる
ストラテジーテスターの最適化機能は、
指定した過去データに対して最も成績が良いパラメーターを見つけ出します。
「その期間だけ」に効くパラメーターになっている可能性
最適化されたパラメーターは、その過去データの偶然のクセに過剰適合している場合があり、
別の期間では効果が消えることがあります。
インサンプルとアウトオブサンプルを分ける
最適化に使う期間(インサンプル)と、検証専用の別期間(アウトオブサンプル)を分けておき、
両方で成績が安定しているかを確認する方法が定番です。
実運用に近づけるための検証手順
4つの原因を踏まえた、実務的な検証手順を確認します。
- ストラテジーテスターはEvery tickモードで実行する
- スプレッド設定を平常時より広い値(例: 平常時の2〜3倍)にしても成績が崩れないか確認する
- 最適化したパラメーターは、別期間(アウトオブサンプル)でも再検証する
- 合格したら、実資金ではなくデモ口座で一定期間(例: 1〜3か月)並走させる
よくある誤解と注意点
解釈でつまずきやすいポイントをまとめます。
バックテストの勝率が高いほど安心、は誤解
勝率やプロフィットファクターが高いバックテスト結果は、
上で見た4つのズレを考慮していない「理想条件での結果」であることを、
前提に見る必要があります。
ヒストリカルデータ自体の欠落もズレの原因になる
出来高が0の期間や時刻が欠落したヒストリカルデータが混ざっていると、
バックテストの精度そのものが下がります。
既刊のiVolume/CopyRatesでデータの欠落を確認する記事も参照してください。
MQL4とMQL5のテスターの違い
MQL5のストラテジーテスターは、
複数通貨ペアの同時テストやより細かいティックシミュレーションに対応しており、
この点はMQL4より実運用に近い設計になっています。
よくある質問
バックテストと実運用のズレについて、よくある疑問に回答します。
Every tickモードにすればズレは無くなりますか?
Every tickモードはOpen prices onlyより実運用に近づきますが、
ズレを完全に無くすものではありません。
スプレッドの拡大・スリッページ・過剰最適化といった、
モデリング方式とは別の原因が残っているためです。
スリッページをコードで再現するにはどうすればいいですか?
ストラテジーテスター自体にはスリッページを再現する標準機能がないため、
テスト結果に数pips分の余裕を持たせて評価したり、
デモ口座での並走期間を設けて、
実際のリクオート発生率を確認する方法が実務的です。
デモ口座での検証はどのくらいの期間行うべきですか?
手法のエントリー頻度によりますが、最低でも1か月、
理想的には異なる相場環境(トレンド期・レンジ期)を含む、
2〜3か月を目安にすることが多いです。
【MQL4】バックテストと実運用の結果がズレる理由についてわかりやすく解説してみた(スプレッド・スリッページ・ティックデータの違い):まとめ
バックテストと実運用の結果がズレる原因は、
ティックデータの粒度・スプレッドの固定化・
スリッページ・過剰最適化の4つに分解できます。
いずれもストラテジーテスターの設計上の制約によるもので、
EAの実装ミスではありません。
Every tickモードでの再検証・スプレッドを広げた再検証・
アウトオブサンプル検証・デモ口座での並走を組み合わせることで、
実運用との差を事前に小さくできます。
コメント