84,120でロング。損切りは84,036、利確は84,271。次の1分足バーは、始値84,118、高値84,290、安値84,010、終値84,240でした。
どちらの水準もこのバーの中にあります。損切りにも利確にも到達していますが、OHLCの4つの数字には、どちらが先だったかを示す情報がまったくありません。それでもバックテストは数値を返しました。ループのどこかに、その判断を下した1行があります。おそらく、書いたときにはモデリング上の仮定だと思いもしなかった1行です。
短期戦略で、私が見てきた中でもっとも大きな見せかけの成績要因がこれです。手数料やスリッページよりも大きい。仮定には見えず、単なる処理のつなぎ込みに見えるからです。
よくある誤りその1:if文の並び順に任せる
よくある形はこうです。
if bar.high >= target:
exit(target, "tp")
elif bar.low <= stop:
exit(stop, "sl")
「利確を損切りより先に処理する」と決めた人はいません。利確の条件が先に書かれたのは、うまくいくケースで、考えていた順番だったからです。2つの分岐を入れ替えるだけで損益曲線が変わります。それだけで、戦略の損益の一部がエディター上の入力順で決まっていると分かるはずです。
BTCUSDT無期限先物を使い、ありふれた平均回帰スキャルパーを意図的に選んで検証しました。1分足を3か月分、4,812回の取引、エントリーから0.10%の損切りと0.18%の利確です。シグナルも手数料も同じで、同時到達時の処理ルールだけを変えました。
取引の8分の1が結果を左右しています。損切りと利確の幅が狭い組み合わせでは、これが起きます。曖昧な取引とは価格が両方向に動いたケースで、注目すべき取引の多くがそうです。どちらを選ぶかで、損切りから利確までの幅がそのまま損益になります。取引の12.6% × 値幅0.28%は、サンプル単位で総取引高の3.5%に相当し、戦略本来のエッジを大きく上回ります。
この割合は、バーの時間幅と損切り・利確の間隔で決まり、バーの時間幅を長くすると急速に悪化します。同じ損切りと利確、同じシグナルを別の時間足にリサンプリングした結果です。
| バーの時間足 | 1本のバー内で両方の水準に到達した取引 | Sharpe(利確を先に処理) |
|---|---|---|
| 1秒 | 0.3% | 0.91 |
| 1分 | 12.6% | 2.31 |
| 5分 | 34% | 3.60 |
| 15分 | 49% | 4.42 |
| 1時間 | 71% | 5.88 |
この表が示していることを見てください。バーの時間幅が長いほど、バックテストの成績が良くなっています。研究者なら誰でも、時間足の長いバーはノイズが少なく、マーケットマイクロストラクチャへの過剰適合も抑えられるため、保守的な選択だと考えるでしょう。しかし、バー内の順序を自分に有利に決めるなら、時間幅の長いバーは「運が良かった」と仮定できる箱が大きくなるだけです。1時間足では、10回の取引のうち7回が、処理ルールだけで決まっています。このバックテストが検証しているのは戦略ではありません。3,400回にわたる2つの`if`文の順序です。
よくある誤りその2:損切り価格で約定したと仮定する
処理順を決めたとしましょう。損切りを先に処理し、ちょうど0.10%の損失にtaker手数料を加えて計上すれば、厳密に見えるかもしれません。それでも、別々の2つの問題が残っています。
1つ目は、ストップ注文はトリガーであり、約定ではないことです。Binance USDⓈ-Mでは、STOP_MARKET注文はトリガー条件を満たした瞬間に成行注文となり、板にある流動性を消費します。静かな1分ならスリッページは1、2ティック程度でしょう。実際に損切りを発動させた1分、つまりローソク足の実体が40ポイントあり、その下で清算の連鎖が起きていた場面では、売買する側の板がまさに薄くなっています。私のサンプルでストップのトリガーをティックデータと照合すると、約定価格はトリガーを中央値で1.4bps、95パーセンタイルで11bps下回りました。10bpsの損切りでは、裾の部分で想定していたリスクのさらに1割を失います。
2つ目はもっと微妙で、無期限先物特有の問題です。どの価格でトリガーされるかです。Binanceではストップ注文のトリガー価格はデフォルトでマーク価格です。マーク価格は指数価格に平滑化したベーシスを加えて算出され、その取引所での直近約定価格からは作られません。OHLC系列が記録するのは直近価格です。両者は別の系列で、乖離がもっとも大きくなるのは、まさにストップを発動させるような局面です。
| 直近価格(klinesのデータ) | マーク価格(デフォルトのトリガー) | |
|---|---|---|
| 参照元 | この取引所での約定 | 複数取引所の指数+ベーシス |
| ヒゲの動き | 値動きをそのまま反映 | 大きく平滑化 |
| 一般的な乖離幅 | 平穏時は1–3bps、清算連鎖の起きた1分では20–35bps | |
| バックテストへの影響 | 本来は発動しないはずの損切りが発動し、その逆も起きる | |
そのため、直近価格のテープに25bpsのヒゲがあれば、バックテストでは損切りになりますが、ライブのマーク価格はトリガーまで10bps以内にも近づいていないことがあります。あるいは、指数価格が動き、取引所側の価格が遅れた日に、その逆が起きることもあります。workingTypeをCONTRACT_PRICEに設定すれば、少なくともライブでの動きを手元のデータに合わせられます。研究者には通常これが適切です。マーク価格でのトリガーを正直にシミュレーションするには、約定エンジン全体で別の価格系列を扱う必要があるからです。
特によく覚えている例があります。チームの誰かが利確幅を0.18%から0.21%に広げて、戦略を「改善」しました。Sharpeは2.3から3.1に上昇しました。新しいエッジが生まれたわけではありません。利確が1分足のヒゲ分布の厚い部分から外れたため、コードがひそかに勝ちを与えていた曖昧な取引が減っただけでした。最適化していたのは同時到達時の処理順だったのです。
よくある誤りその3:常に最悪を想定し、それを保守的と呼ぶ
すぐ思いつく対策は悲観的に処理することです。両方の水準に到達したら損切りにする。これで楽観的な見積もりはなくなり、完成です。
以前は私もそうしていました。別のやり方よりはましですが、それでも間違いです。理由は2つあります。
問題ない戦略まで潰してしまいます。12.6%の取引を悲観的に処理した結果、この戦略のSharpeは、ティックデータで判定した0.94に対して2.1ポイント下がりました。本当の数値が0.94なのに、処理ルールのせいで0.18と出れば、そのアイデアを捨てて、もっと悪いものに取り組むことになります。Sharpeが2ポイントもずれる保守性は、保守性ではありません。道徳的な顔をしたノイズです。
さらに悪いことに、最適化が歪みます。悲観的な同時到達ルールをパラメータ探索に与えると、曖昧さが純粋なペナルティになるため、最適化器は曖昧なケースを避けるよう学習します。損切り幅を広げて利確幅を狭める方向や、2つの水準が同じバーに入りにくい時間足の長い方向へ進み、約定ルールに合わせて選ばれたパラメータを、市場に合わせたものとして提示します。寛大なルールで起きる失敗と同じで、符号が逆なだけです。成績レポートではどちらも見えません。
まず使う経験則はこれです。stop_distance + target_distanceがバーの時間足の値幅の75パーセンタイルを下回るなら、バー内の仮定が損益に与える影響はシグナルより大きくなります。両方の数字を計算してください。4行で済み、ほかのどんなチェックよりも多く戦略レビューの結論を覆してきました。
実際に有効な方法
バーの中の価格経路もデータです。取得するか、取得できない部分の影響範囲を示しましょう。
- 手元にある最も細かい価格系列で判定する。該当する数分間のBinance aggTradesなら数百行程度で、どちらの水準に先に到達したか、急変時にどの価格で約定したかがはっきり分かります。バックテスト全体にティックデータは必要ありません。曖昧なバーだけで十分です。私のサンプルでは129,600分のうち606分でした。大がかりなインフラ整備ではなく、小さなダウンロードで済みます。
- ティックデータがなければ、判定時だけ時間足を1段階か2段階下げる。シグナルは15分足で出し、決済は1秒足または1分足で判定します。曖昧なケースは49%から1%未満に減り、残りは正直に無視できる程度です。
- 必ず幅で報告する。バックテストを楽観的な判定と悲観的な判定の2通りで実行し、判定済みのSharpeと並べて両方を表示します。その差がバー内の不確実性です。Sharpe自体につける信頼区間と同じように、成績レポートに載せましょう。幅が0.2–2.3なら、その範囲内の結論に信頼性はありません。
- 曖昧な取引の割合を主要指標として追跡する。私たちは各戦略カードの最上部に、取引回数や取引高と並べて表示しています。割合が約5%を超えるなら、検証されているのはエントリーではなく決済ロジックです。
- トリガーと約定を別々にモデル化する。トリガーには取引所が実際に使う価格系列を使い、約定価格はトリガー価格に固定せず、テープから推定したスリッページを加味して設定します。
株式にも同じ問題がありますが、形が違います。62.00に損切りを置いた銘柄が夜間に58.40まで窓を開けて下落した場合、62.00では約定しません。寄り付きの価格より低いどこかで約定します。窓開け時のスリッページを−$0.00として計上する日足バックテストは、損切りオーバーレイでドローダウンが改善したと喜んで報告するかもしれません。改善したわけではありません。重要な日に一度も検証されていないだけです。取引停止でも同じことが起きます。損切りが実際に決済されるのは再開後のオークションであり、その価格はバーの安値には現れません。
どれも特別な話ではありません。バーは要約であり、損切りと利確を使う戦略は、その要約から消えたイベントの順序に賭けているということです。ペーパートレードのエンジンがライブの価格テープで戦略を動かせば、テープはその順序について明確な答えを示します。あなたがif文のどちらの分岐を先に書いたかなど、一度も気にしたことはありません。
← 記事一覧


