メインコンテンツにスキップ
エンジニアリングバックテストアルゴリズム
バックテストが嘘をつく理由:インジケーターベースの決済シミュレーションは見た目より難しい

バックテストが嘘をつく理由:インジケーターベースの決済シミュレーションは見た目より難しい

バックテストは数値を出力します。そして数値には説得力があります。それこそが問題の核心です。シミュレーションがわずかに間違っているとき、それはクラッシュもしなければ警告もしてくれません。実際よりも少しだけ良い結果を差し出し、あなたにそれを元に行動させてしまいます。

固定の利益確定をシミュレートするのは簡単です。価格があなたの水準に到達したか、しなかったか、それだけです。難しくなるのはインジケーターベースのエントリーや決済をシミュレートするときです。なぜなら、そこでシミュレーションははるかに厄介な問いに答えなければならないからです。その瞬間、ボットは実際に何を知り得たのか?

ここでは、私たちがその問いを取り違えた3つのケースと、それぞれが教えてくれたことを紹介します。

要点

  • 典型的なバグは、上位時間足のインジケーターをそのローソク足が確定する前に読んでしまうことです。ローソク足の始値時点で参照した4時間足のRSIは、まだ起きていない4時間分の値動きを含んでいます。
  • 修正はヒューリスティックではなく算術です。時間足Tの値が正当に使えるのはTが経過した後だけなので、参照を(トリガー時間足からシミュレーション時間足を引いた分)だけ過去にずらします。
  • ライブエンジンも、姿を変えた同じ問題を抱えています。まだ形成中のローソク足で計算したインジケーターはリペイントします。つまり、ある条件が同じローソク足の中で真になり、その後に偽になり得るのです。
  • 再現できないものを黙ってスキップするシミュレーターは、ライブのボットよりも条件が甘くなります。そしてそのズレは常にバックテストに有利に働きます。
  • 誠実なやり方は、そのズレを隠すのではなく結果の中で明示することです。どこで嘘をついているかを教えてくれるバックテストは、きれいに見えるだけのものよりも価値があります。

1. マルチタイムフレーム戦略に潜む先読みバグ

これは、最も書きやすく、最も気づきにくいバグです。

シミュレーションが5分足のローソク足を1本ずつ前に進めていき、戦略が4時間足のRSIに条件を持っているとしましょう。5分ごとのステップで、エンジンはそのRSIの値を必要とします。素朴な実装は、現在時点を含む4時間足のローソク足を参照し、その値を読み取ります。

それはタイムマシンです。

12:00に始まった4時間足のローソク足は、16:00に確定するまで最終的なRSIの値を持ちません。シミュレーションが12:05の地点に立ってそのローソク足を読むなら、それは12:05の視点からはまだ起きていない4時間分の値動きから計算された数値を読んでいることになります。戦略は、予見できたはずのない値動きの直前でエントリーしているように見え、資産曲線は素晴らしく見えます。

修正はヒューリスティックではありません。ある値が実際にいつ知り得るのかを書き出せば、自然に導かれます。triggerMsの時間足でtに始まるローソク足は、t + triggerMsになって初めて知り得ます。シミュレーションは現在のローソク足の確定時点、つまりcandleOpenTime + simulationMsで判断を下します。したがって、その値が正当に使えるのは、ちょうど次の条件が成り立つときです:

t + triggerMs <= candleOpenTime + simulationMs
t             <= candleOpenTime - (triggerMs - simulationMs)

つまり正しい参照とは、すでに持っていた参照を、triggerMs - simulationMsだけ過去にずらしたタイムスタンプに対して実行したものです。

この式には、あらゆる場面で安全に適用できる理由となる2つの性質があり、指摘しておく価値があります:

  • 同一時間足の条件はシフトがゼロになります。 5分足のローソク足の確定時点で読む5分足のインジケーターは、それ自身の確定値であり、常に正しかったものです。動くのは本当に上位の時間足だけです。
  • シフトは設定されるものではなく、導出されるものです。 それは誰かが忘れずに設定しなければならない設定値ではなく、ローソク足そのものから得られます。

ここから得られる一般的な教訓は、取引に限らずあらゆるシミュレーションに当てはめたいものです。先読みバイアスがあるかどうか自信が持てないときは、各入力がいつ知り得るようになるかのタイムスタンプを書き出し、それを実際に使うタイムスタンプと比較してください。 そのステップを飛ばして代わりに直感に頼ったときに残るものがバイアスです。そして直感はこの点で頼りになりません。なぜなら「現在の4時間足のローソク足」を読むという発想は、まったく理にかなっているように聞こえるからです。

2. 同じバグはライブコードでは別の装いをまとう

この問題はバックテストに固有のものだと思うかもしれません。そうではありません。ライブエンジンもこれに直面し、そこではリペイントと呼ばれます。

どの瞬間においても、最新のローソク足はまだ形成中です。そのローソク足で終わるウィンドウで計算されたインジケーターは、ローソク足が変化するにつれて変化します。ある条件が12:01に真、12:03に偽、そして12:04に再び真になり、しかもそれがすべて同じローソク足の中で、市場が何ら異常なことをしていなくても起こり得ます。ボットがそのうち最初のものを元に行動したなら、それはもはや存在しない値を元に行動したことになります。

私たちのデフォルトは、まだ形成中のローソク足を捨て、確定したローソク足のみで計算することです。こうすればシグナルはリペイントしません。これはライブとバックテストを一致させる要因でもあります。なぜならシミュレーションは構造上、確定したローソク足を扱っているからです。

しかし「リペイントしない」ことはタダではありません。確定を待つということは行動が遅れるということであり、一部の戦略ではその遅れが、避けようとするノイズよりも高くつきます。そこでこれは、条件ごとの選択肢として公開されています。確定したローソク足で評価するか、ライブで評価して値が自分の足元で動き得ることを受け入れるか、です。

設計上の要点として残しておきたいのはこの部分です。トレードオフが本当にトレードオフであるなら、それをデフォルトの中に埋め込んで事実であるかのように装ってはいけません。 安全な選択肢をデフォルトにし、その上でユーザーがもう一方を承知の上で選べるようにします。やってはいけないのは、この2つのモードをライブとバックテストの間で食い違わせることです。そうなると、ユーザーは2つの異なるボットを比較していることになるからです。

3. シミュレーターが再現できないものは、間違えるものよりも危険

最初の2つは正しさに関するバグです。これは設計上の問題であり、最も過小評価されていると私が考えるものです。

再現できない条件があります。外部のWebhookシグナルが最も分かりやすい例です。外部のシステムが去年の3月のある日の14:20に何を送ってきたはずか、その過去の記録は存在しません。シミュレーションには単純に知りようがないのです。

では、どうすべきでしょうか? 魅力的に見える答えは、その条件をスキップして残りを評価することです。それが結果を完全なものに見せます。しかしそこで損害が起こるのであり、その仕組みは書き出しておく価値があります:

戦略がAかつBを必要とし、Bがシミュレートできない場合、BをスキップするとAだけが単独で判断することになります。シミュレーション上のボットは、ライブのボットならエントリーを見送っていた条件でエントリーします。シミュレーションは、実物よりも体系的に条件が甘くなります。

その方向性に注目してください。この誤差はランダムではありません。ANDロジックの下で条件を1つ落とすと、生み出される取引は実際よりも多くなることしかなく、少なくなることは決してありません。だからこのバイアスは、毎回きまって見栄えの良い結果を指し示します。この非対称性こそが危険な点です。ランダムなノイズは何度も繰り返せば平均化されますが、一方向のバイアスは平均化されません。

私たちは、拒否するのではなくシミュレーションを続行することを選びました。なぜなら、既知のズレを含む部分的な結果であっても依然として有用だからです。しかしその場合、ズレはログの中に埋もれるのではなく、出力の中で見えるようになっていなければなりません。結果には、ユーザー自身の言語で、何が起きたかを示す名前とともに、明示的な警告が付きます。**「一部のエントリー条件がシミュレートされていません」**です。決済についても対応するものがあります。

この決定は、このシステムの中で私が最も強く擁護したい部分です。大きな音を立てて失敗できないシミュレーターは、静かに失敗します。そして数値を生み出すものにおける静かな失敗は、存在する中で最悪の失敗モードです。 なぜなら、人々は数値を元に行動し、スタックトレースを元には行動しないからです。

ここから何を持ち帰るか

数値を生み出すために過去を再現する何かを構築しているなら:

  • すべての入力には「いつ知り得るか」のタイムスタンプが必要です。 それを言明できないなら、先読みバイアスを排除できません。
  • 誤差の大きさだけでなく、その方向を確認してください。 常に同じ方向を指す誤差は積み重なります。ランダムな方向を指す誤差は平均化されます。一方向の楽観は、先読みバイアスの特徴です。
  • 可能な限り、シミュレーターとライブシステムに判断ロジックを共有させてください。 「この条件は真か」の実装が2つあれば、それらは食い違っていき、あなたはテストからではなくユーザーからそれを知らされることになります。
  • できなかったことを報告してください。 結果の信頼性は、その明示された限界から来るのであり、見た目のきれいさから来るのではありません。

居心地の悪い要約をすれば、バックテストとは反実仮想についての主張であり、反実仮想はひそかに間違えやすいものです。エンジニアリングの目標は、決して嘘をつかないシミュレーターではありません。どこで嘘をついているかを教えてくれるシミュレーターです。


関連記事

学んだことを実践しよう。

無料アカウントを作成し、戦略をテストして、最初のボットを起動しましょう。

無料で始める
このコンテンツは教育目的のみであり、金融アドバイスではありません。仮想通貨取引には重大なリスクが伴います。失っても許容できる金額以上の投資はしないでください。