cTrader バックテストの実行手順 — ティックデータ精度とスプレッド設定で結果はどう変わるか

cBot を書き終えて最初に走らせるのがバックテストですが、cTrader Automate のバックテストは設定項目が結果を大きく動かします。同じコード・同じ期間でも、データ精度とスプレッドの与え方を変えるだけで損益曲線の形が変わることは珍しくありません。

本記事では、cTrader でバックテストを実行する手順を整理したうえで、結果を歪める代表的な要因と、コード側で防げる部分を具体的に見ていきます。バックテストという手法そのものの前提は バックテストとは|cBot開発で押さえたい検証の基本と注意点 で扱っているので、あわせて参照してください。

バックテストを始める前に決める4項目

cTrader Automate のバックテストタブでは、実行前に少なくとも次の4つを決めることになります。

  1. 銘柄とタイムフレーム — cBot が想定している時間軸と一致しているか
  2. 期間 — 何年分を対象にするか
  3. データ精度 — ティック単位で再現するか、m1 バーから合成するか
  4. 初期資金・スプレッド・コミッション — 約定コストをどう与えるか

このうち3と4は、初期値のまま流してしまいがちですが、後述するとおり結果への影響が最も大きい部分です。銘柄とタイムフレームは cBot 側の想定と食い違っていないかを確認します。

データ精度をどう選ぶか

cTrader のバックテストは、価格データの与え方を複数のモードから選べます。大きく分けると、m1 バーから値動きを合成する方式と、サーバーから取得したティックデータをそのまま流す方式です。

m1 バー方式は高速ですが、1分足の四本値から足の中の動きを推定するため、足の中でストップとテイクプロフィットの両方に触れる場面で、どちらが先に約定したかが実際とずれる可能性があります。ストップ幅が狭い戦略、あるいは指標発表時の急変を跨ぐ戦略ほど、この差は無視できなくなります。

ティックデータ方式は実行時間が大幅に伸びる代わりに、足の中の順序を再現できます。判断としては、開発中の試行錯誤は m1 バーで速く回し、採否を決める最終確認はティックデータで取り直すという使い分けが現実的です。両者で成績が大きく食い違う戦略は、そもそも約定タイミングに依存しすぎている疑いがあります。

なお、利用できる期間はブローカーが保持しているデータの範囲に依存します。取得できる年数はプラットフォームや口座によって異なるため、実際に選択できる期間を確認したうえで計画を立ててください。

スプレッドとコミッションが成績を変える

バックテストでスプレッドを固定値で与えると、実際の変動を再現できません。日本時間の早朝や指標発表の前後はスプレッドが平常時の数倍に開くことがあり、その時間帯にエントリーする設計の cBot は、固定スプレッドのバックテストでは実態より良い数字が出ます。

コミッションも同様です。銘柄ごとの往復コストを設定に反映していないと、取引回数が多い短期の設計ほど乖離が大きくなります。取引1回あたりの損益が小さい設計では、コストの入れ忘れだけで損益の符号が反転することもあります。

対処としては、次のような確認が有効です。

関連する基礎は スプレッドとはスリッページとは で整理しています。

コード側でルックアヘッドを持ち込まない

設定以前に、コードが未来の情報を参照していると、バックテストの数字は現実には再現できないものになります。cTrader で最も起きやすいのは、まだ確定していない足を判定に使ってしまうパターンです。

OnBar() が呼ばれた時点で、インデックス Bars.Count - 1 は「いま開いたばかりの足」です。この足の終値はまだ決まっていません。判定に使ってよいのは確定済みの Bars.Count - 2 です。

using cAlgo.API;
using cAlgo.API.Indicators;

namespace cAlgo.Robots
{
    [Robot(AccessRights = AccessRights.None)]
    public class BarCloseEntry : Robot
    {
        [Parameter("MA Period", DefaultValue = 20, MinValue = 2)]
        public int MaPeriod { get; set; }

        private MovingAverage _ma;

        protected override void OnStart()
        {
            _ma = Indicators.MovingAverage(Bars.ClosePrices, MaPeriod, MovingAverageType.Simple);
        }

        protected override void OnBar()
        {
            // Bars.Count - 1 は開いたばかりの足。終値はまだ確定していない。
            // 判定に使ってよいのは確定済みの Bars.Count - 2。
            int i = Bars.Count - 2;
            if (i < MaPeriod) return;

            double close = Bars.ClosePrices[i];
            double ma    = _ma.Result[i];

            Print("time={0} close={1} ma={2}", Bars.OpenTimes[i], close, ma);
        }
    }
}

ライブでは形成途中の足を読んでも「その時点の値」しか取れないため気づきにくく、バックテストでだけ成績が良くなるという形で現れます。バックテストとライブの乖離を調べるときは、まずここを疑うと切り分けが早くなります。

ロットが意図せず膨らむ落とし穴

リスク率からロットを計算する設計では、Symbol.NormalizeVolumeInUnits の挙動に注意が必要です。計算結果が最小取引単位を下回った場合、0 になるのではなく最小単位へ引き上げられます。

つまり「資金が小さい」「ストップが広い」といった理由で本来は見送るべき場面でも、最小ロットで発注が成立し続けます。バックテスト全体で見ると、想定よりリスクの大きい取引が混ざった状態の成績を見ていることになります。

double riskAmount = Account.Balance * RiskPercent / 100.0;
double volume     = riskAmount / (StopLossPips * Symbol.PipValue);

double normalized = Symbol.NormalizeVolumeInUnits(volume, RoundingMode.Down);

// 最小未満は 0 ではなく最小へ切り上がるため、明示的に弾く。
if (volume < Symbol.VolumeInUnitsMin)
{
    Print("計算ロット {0} が最小 {1} 未満のため発注を見送ります。",
          volume, Symbol.VolumeInUnitsMin);
    return;
}

normalized を判定に使うと切り上げ後の値になってしまうため、切り上げ前の volume で判定するのが要点です。ロットの考え方は ロットとは で整理しています。

取引ごとの記録を外に出す

cTrader の集計画面だけでは、どの時間帯・どの局面で損益が偏っているかまでは追いにくいことがあります。決済イベントで1取引ずつ出力しておくと、表計算ソフトで期間や曜日ごとに切り直せます。

protected override void OnStart()
{
    Positions.Closed += OnPositionsClosed;
}

private void OnPositionsClosed(PositionClosedEventArgs args)
{
    var p = args.Position;
    Print("{0},{1},{2},{3},{4},{5},{6}",
          p.EntryTime, Server.Time, p.SymbolName, p.TradeType,
          p.VolumeInUnits, p.Pips, p.NetProfit);
}

取引数が十分にあるかどうかは、成績を読む前に確認しておきたい点です。数十件の結果から傾向を語ると、たまたまの偏りを実力と取り違えやすくなります。

期間を分けて確かめる

cTrader Automate には、いわゆるウォークフォワード検証を自動で回す機能は用意されていません。そのため、期間を手で分けて別々に実行するという手順を自分で組む必要があります。

具体的には、パラメーターを決めるために使う期間と、決めたあとに一度も触っていない期間を分けます。前者で良い数字が出るのは当然なので、判断材料になるのは後者だけです。Optimization 機能でパラメーターを探索した場合はとくに、探索に使った期間の成績は評価に使えません。

このとき、合否の基準は探索を始める前に決めておくことが重要です。結果を見てから基準を決めると、良かった数字に合わせて基準のほうが動いてしまいます。プロフィットファクターや最大ドローダウンの見方は プロフィットファクターとはドローダウンとは を参照してください。

まとめ

cTrader のバックテストで結果を歪める要因は、次の順で確認すると切り分けやすくなります。

確認する順序見るところ
1コードが確定していない足を読んでいないか
2ロット計算が最小単位へ切り上がっていないか
3スプレッド・コミッションが実態に近いか
4データ精度がティックとバーでどれだけ食い違うか
5探索に使っていない期間でも成立するか

数字が良くなったときほど、改善したのか、それとも過去に合わせ込んだだけなのかを分けて考える必要があります。バックテストは判断を助ける道具であって、将来の成績を示すものではありません。

cTrader 向けの cBot・インジケーターの開発や、既存ロジックの検証については ai-programming.xyz でも扱っています。開発の進め方を体系的に学びたい場合は 未経験から cBot を自作するまでの学習ロードマップ もあわせてご覧ください。

本記事は一般的な情報提供を目的としています。投資助言ではありません。 取引判断はご自身の責任で行ってください。