メインコンテンツにスキップ
エンジニアリングバックテスト取引ボット
ノーコード取引ボットプラットフォームを作って過小評価していたこと

ノーコード取引ボットプラットフォームを作って過小評価していたこと

ノーコード取引ボットの売り文句はシンプルです:フォームで戦略を記述すれば、ソフトウェアが代わりに実行してくれます。難しいのはフォームではありません。ユーザーに提供する簡略化のひとつひとつが、システムのどこかで、たいていは予想もしなかった場所で代償を払わされる、という点です。

以下は、私がその点で読み違えていた3つと、それぞれが実際に教えてくれたことです。誤判断のひどかった順に並べています。

要点

  • 取引所のAPIは、派手に失敗するよりも静かに失敗することのほうが多いです。要求したよりも少ないデータを返すHTTP 200こそ、設計時に備えるべき失敗パターンです。
  • シミュレーター自体をテストするまでは、バックテスト結果を決して信用しないでください。きれいに見える資産曲線が、壊れた実行から生まれることもあります。
  • 注文数量の浮動小数点演算は、特定の通貨ペアでのみ現れる1ステップずれの誤差を生みます。
  • ユーザー資金を一切預からないと決めることで、まるごと一群の問題が消え、代わりにより小さく鋭い問題が生まれます:システムは何ひとつ取り消せないまま、常に正しく動く必要があります。
  • ユーザーに提供する利便性はすべて、エンジンのどこかで制約に変わります。そのトレードオフはたいてい引き受ける価値がありますが、決して無料ではありません。

1. 失敗したリクエストは失敗の見た目をしていると思い込んでいた

これは最も時間を奪ったものであり、どんな開発者にも真っ先に警告したいものです。

私たちはバックテストを実行するために、取引所から過去のローソク足を取得します。素朴なメンタルモデルでは、リクエストは成功するかエラーになるかのどちらかです:データが得られるか、200以外が返ってきて処理するかです。実際の取引所APIは、負荷がかかるとそのようには振る舞いません。要求したよりも少ないローソク足を伴うHTTP 200を返し、何かがおかしいという兆候はまったくありません。

ログから取った実例を挙げます。数値は正確です。長期間のバックテストがおよそ536,000本のローソク足を要求しました。16:34には58,000本を受け取りました。ステータスは200。エラーフィールドなし。警告なし。まったく同じリクエストが、17:54には535,000本すべてを受け取りました。

チェックしていなかったら何が起きるか考えてみてください。エンジンは意図した履歴の11パーセントに対して何食わぬ顔で戦略をシミュレートし、もっともらしい資産曲線を生成し、結果を報告します。ユーザーは、出力のどこからもそうとは分からない形で間違っている数値を読むことになります。これはクラッシュより悪いです。クラッシュは正直だからです。

修正は概念的には退屈で、つい飛ばしてしまいがちでした:取得後に、受け取ったデータが要求した期間を実際にカバーしているか検証し、通信層は問題ないと言っていても不足があれば失敗として扱う、というものです。カバー範囲は、当然のものと仮定できるのではなく、明示的に確認しなければならない性質のものです。

最初の教訓の裏には、二次的な教訓が隠れていました。切り詰めを検出し始めると、当然の一手は実行を失敗させることです。しかし上記の数値が示すとおり、これらの切り詰めは一時的です:1時間後にまったく同じリクエストが完全なデータを返しました。すぐに失敗させてしまうと、取引所側の一時的なつまずきにすぎないもののためにユーザーのクォータを浪費することになります。だから正しい振る舞いは、失敗させる前に一度リトライすることです。これは、リトライ可能な状態が処理の途中で恒久的な失敗として記録されてしまわないよう、失敗経路を作り直すことを意味しました。

ここから2つのルールが生まれました。サードパーティのAPIと通信するどのプロジェクトにも、私はこの両方を持ち込みます:

  • ステータスコードだけでなく、成功レスポンスの形も検証してください。 ステータスコードはリクエストが完了したことを教えてくれます。レスポンスが要求どおりのものだとは教えてくれません。
  • どう対処するか決める前に、一時的な失敗と恒久的な失敗を区別してください。 両者は発生した瞬間には見分けがつきませんが、正反対の対応を必要とします。

2. バックテスト結果は結果だと思い込んでいた

関連していますが別の間違いであり、切り詰め問題よりも根が深いものです。

シミュレーターを作るとき、テストの労力は戦略に注がれます。DCAロジックは正しく振る舞うか?退出は然るべきところで発動するか?それが面白い問いなので、自然と注目もそこに向かいます。

問題は、壊れたシミュレーターが、正常に動くものとまったく同じ見た目の出力を生むことです。データの一部だけを黙って処理した実行も、やはり数値を出します。退出条件が一度も評価されなかった実行も、やはり数値を出しますが、それは疑わしいほど平坦な数値です。取引ゼロでちょうど+0.00%という結果が返ってきたのを見たことがあります。これは機構に何か問題があるという、考えうる限り最も大きな信号であり、同時に「この戦略は何もしない」と最も誤読されやすいものでもあります。

これを直した転換は、何をテストするかを変えたことでした。戦略ではなく、シミュレーターをテストする。 具体的には、戦略が変えられるはずのないものについて表明する、ということです:処理したローソク足の本数が取得した本数と一致すること、既知の期間にわたる実行が実際にその期間をカバーすること、必ずトリガーされる設定がちゃんと取引を生むこと。これらの不変条件が成り立つとき、奇妙に見える結果は戦略についての本物の発見です。成り立たないとき、その結果はスーツを着たノイズにすぎません。

何かをシミュレートするものすべてに当てはまる一般形はこうです:派手に失敗できないシミュレーターは、静かに失敗します。そして数値を生み出すシステムにおける静かな失敗は、存在しうる最悪の失敗パターンです。 人は数値をもとに行動します。スタックトレースをもとには行動しません。

3. 注文サイズの精度作業がどれほどのものか過小評価していた

これは知的に難しいわけではありません。ただ見た目よりずっと大きく、しかも間違いの代償が高い場所に潜んでいます。

取引所は任意の数量を受け付けません。各取引ペアには数量のステップサイズと価格のティックサイズがあり、それに揃っていない注文は拒否されます。だからシステムが計算するすべての数量は、送信前に有効なステップへ切り捨てる必要があります。これは1つのユーティリティ関数のように聞こえます。

そこに浮動小数点が登場します。0.29をステップ0.01へ切り捨てる場合を考えてみてください。スケールアップするとIEEE-754は28.999999999999996を返し、これは28に切り捨てられ、0.28にスケールし直されます。この値はもともとステップに揃っていました。演算がそれを丸ごと1ステップ下げてしまったのです。しかも静かに、より小さい注文の方向へ。修正にはスケーリング段階でイプシロンが必要です。0.289999のような本当に下回っている値は正しく切り捨てられる程度に小さく、表現上のノイズを吸収できる程度に大きいイプシロンです。

これは1行の修正ですが、見つけるには本物のデバッグが必要でした。症状が例外ではないからです。それは意図よりわずかに小さい注文であり、ステップサイズが2進表現に対してどこに位置するかによって、あるペアでは起き、別のペアでは起きません。

より大きな論点、そしてこれを過小評価したものとして挙げる理由はこうです:取引システムにおける精度は、ユーティリティ関数ではなく、作業のカテゴリーそのものです。 ステップサイズとティックサイズは、ペアごと、取引所ごと、そしてスポットと先物の間で異なります。最小注文額も、調べてハードコードできる1つの数字ではありません。それらは取引所からシンボルごとに提供され、一部の取引所では現在価格に依存します。取引所がペアごとに定義するものに対して単一の定数を書いていると気づいたなら、それは時限信管付きのバグを書いているということです。

私たちはこれを隠すのではなく、ユーザーに見えるようにしています。そうしない場合の代わりは、ユーザーには見えない理由で拒否される注文だからです。

4. 気に入るとは思っていなかった制約

最後の1つは間違いではありません。良い意味でその帰結を過小評価していた決断です。

Freyaはユーザーの取引資金を一切預かりません。ボットは、読み取りと取引の権限を持ち、出金の権限は決して持たない取引所APIキーを通じて取引します。資金は最初から最後までユーザーの取引所口座にとどまります。

これはマーケティング上のポイントだと思っていました。ところがアーキテクチャ上のポイントだと分かり、システムを小さくしてくれました。カストディ台帳もなく、外部残高と突き合わせるべき内部残高もなく、出金キューもなく、取引資本のためのコールドウォレット方針もありません。金融ソフトウェアで最も難しい問題のまるごと一群が、単に存在しません。それらを生み出すものを、私たちが最初から引き受けなかったからです。

その代わりに来るのは、より狭く鋭いものです:システムは何も取り消せないので、リアルタイムで正しくなければなりません。 カストディがあれば、原理的には内部送金を巻き戻せます。他人の取引所口座に注文を出すことは、約定した瞬間に確定します。これは、エンジニアリングの重みをすべて、事後の突き合わせではなく、リクエストを送る前の正しさへと寄せます。これは、もう一度でも選ぶトレードオフです。

始めた頃の自分に伝えたいこと

  • すべてのサードパーティAPIは、ときどき間違ったまま成功すると想定し、必要になる前に検出の仕組みを設計してください。
  • 戦略についての表明を書く前に、シミュレーターの不変条件テストを書いてください。シミュレーターのテストが通るまで、戦略のテストは無価値です。
  • 精度をヘルパーではなく、1つのドメインとして扱ってください。
  • 制約は早く選んでください。最初に受け入れる制約は、後で発見する制約よりずっと安上がりです。

どれも風変わりな問題ではありませんでした。計画中に「小さい」と分類した、ありふれたエンジニアリングの問題です。そしてまさにそれが、これらが高くついた理由です。


関連記事

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

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

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