コンテンツへスキップ

3つのバグ、3つの合格したテスト、検出はゼロ

ETHUSDT無期限先物の4時間足チャートに、2つのルックバック期間を並べて描いたもの。一方は23本、もう一方は22本で、これはPineのソースが計算する長さと、同じ式からPythonの作り直しが計算した長さです。
同じルックバックを2回計算しています。Pineは22.5をゼロから遠ざける向きに丸めて23を返します。Python 3は偶数側に丸めて22を返します。どちらの言語でも、慣習が変わったことを警告してくれるものは何もありません。

バックテストのためにインジケーターをPythonへ移植したことがある方、あるいは検証が付いているからという理由でスクリプトを信用したことがある方へ。本稿は、その2つが開けたままにしている隙間についての話です。

バックテストを走らせるために、自社のインジケーターを3つ作り直しました。3つとも中に誤った定数を抱えていました。3つとも検証に合格しました。テストに捕まったものは1つもありませんでした。

そもそもなぜインジケーターを作り直すのか

Pineスクリプトはあなたのチャート上で動きます。6年分の履歴をループで走り抜けることはしませんし、シグナルが実際に割に合うのかにも答えません。それを問うには、何千回でも実行できる環境で作り直すことになります。

インジケーターが静かに同じインジケーターでなくなるのは、まさにその作り直しの場面です。

最も高くついたバグ、それも1本分

Volume Oracleの内部にある長さの1つは、時間軸から導かれます。

target bars   = 36 hours × 60 / 240 minutes = 9
regime length = round(9 × 2.5) = round(22.5)

Pineの math.round は、ちょうど中間の値をゼロから遠ざける向きに決めます。返すのは 23。Python 3の round() は銀行家の丸めを使い、中間の値を偶数側に決めます。返すのは 22.

どちらも丸めの正しい実装です。単に慣習が違うだけであり、一方から他方へ渡ってしまったことを警告してくれるものは何もありません。

その数から導かれたすべてのルックバックが、その数とともに動きました。36シグナルの走査のうち、最も裏づけの厚い行での結果は次のとおりです。

バージョン48時間の優位勝率n
作り直し、長さ22+0.423%511216
正しい版、長さ23+0.260%501223

丸めの慣習だけで38%の目減りです。

そのうえ教訓の記録も誤っていた

私たちが書いたメモには、4Hでのみ起きると記されていました。たまたまテスト中だった唯一の時間軸から書かれたもので、そのまま2か月置かれていました。

同じ計算をすべての時間軸で走らせると、こうなります。

時間軸レジーム長フロー長結果
5m45 対 4523 対 22食い違う
2H45 対 4523 対 22食い違う
4H23 対 2212 対 11食い違う
8H53 対 5227 対 26食い違う
1D18 対 189 対 9中間値、一致する
3D, 1W15 対 158 対 8中間値、一致する

16のうち4つであって、1つではありません。

この表からは、いくら考えても出てこなかった2つのことが落ちてきます。

中間値であるだけでは足りません。 1D、3D、1Wはいずれもちょうど.5に着地し、しかも一致します。銀行家の丸めが動かすのは、切り上げた答えが奇数になる中間値だけなので、 round(17.5) はどちらでも18です。中間値のおよそ半分は無害であり、だからこそ1つを抜き取って確かめても何の証明にもなりません。

食い違いは1手遅れて届くことがあります。 5mと2Hでは、最初の長さは45で一致します。その次の行、 round(45 × 0.5)がまた22.5になり、そちらが割れます。最初の定数だけ確かめて先へ進めば、2つの時間軸をどちらも誤って合格にしてしまいます。

残りの2つ、手短に

Harmonic はオシレーターを移動バンドで切り詰めて正規化します。ソースでは各バーが自分のバンドで切り詰められます。作り直した版は、現在のバーのバンドを使って200本の窓全体を切り詰めており、これは別の関数です。11,497本で測った差は平均0.47ポイント。実害のない、本物の写し間違いです。

しかもこれは、測る前に膨らませてしまいました。コードを読んだだけで、ほぼ確実に例の9%のずれだと宣言したのです。数字は0.47でした。本物のバグと実害のあるバグは別の主張であり、影響に名前を付ける前に大きさを測ります。

Plutus Flow は出来高を50本で平均して急増を見つけます。作り直した版は20本を使っていました。それが知っているつもりの数字だったからです。影響は0.1パーセントポイント未満で、符号の反転もありません。結論は動きませんでしたが、数字は誤っていて今は正しい。重要な基準はそれだけです。

ここからが本当の教訓

当時、それぞれの作り直しの検証が報告した内容は次のとおりです。

作り直し自身のチェックそれが意味したこと
Volume Oracle修正前3/3、修正後3/3両者を区別できなかった
Harmonic修正前6/8、修正後5/8バグのある版により高い点を付けた
Plutus Flow一度も実行されず

腰を据えて見るべきは真ん中の行です。検証は誤ったコードのほうを好みました。チェックに決めさせていたら、理屈でバグのほうへ引き戻されていたでしょう。

出力値を1つ比べるだけの検証では、入力側の誤った定数は検出できません。 出力はふるいとして粗すぎます。多数の異なる誤ったパラメーターの組が、今日のチャート上では同じレジームのラベル、同じクロスの状態、同じ色を生みます。違いが出るのは、あなたが今見ていないバーの上であり、バックテストが生きているのはまさにそこです。

代わりに何をするか

導出されたパラメーターをすべて出力してください。すべての長さ、すべてのしきい値、すべての倍率を。何かを採点する前に、ソースと1行ずつ突き合わせてください。

出力チェックは残してください。別の種類の誤りを捕まえますし、必要です。ただ、十分にはほど遠く、今回の3つはその前を素通りしていきました。

正直な適用範囲

Pineのコードは3件とも正しいものでした。誤っていたのは、それを試すために組んだ装置のほうです。これは問題のより居心地の悪い形です。測定器がずれ、その測定器自身の自己点検はずれていないと答えていました。

38%という数字は、1行、1回の走査、1つの時間軸、標本内のものであり、再実行ではなく自社の記録から引用しています。丸めの結果はコマンド1つで再現でき、それを直す過程で、自分たちのメモが2か月間誤っていた事実が出てきました。


自社ツールに対する過去の測定を、事後に記述したものです。ここにあるものはいずれも推奨ではなく、将来の結果を示すものでもありません。