前 2 回の記事に引き続き、プログラマティック取引で注意が必要な事項について説明します。

ハードコア乾物~定量取引システムの自動リアルタイム取引の詳細と考え方(1.問題点と困難さ)

定量取引システム - 自動ファームオファーの詳細と考え方 (2. ファームオファーの目的)

ここでは引き続き、コード設計における詳細な処理スキルについて説明します。

データベース

前の 2 つの記事で述べたように、より複雑な戦略の組み合わせのステータス記録は非常に重要であり、データベースの使用が必要です。

実際、ほとんどのプログラミング ビジネスは CRUD カテゴリに分類できます。つまり、中心的なプロセスはデータベースの追加、削除、変更、チェックだけです。取引コードも例外ではありません。中低頻度の流通市場の取引行動は、実際には、人々が特定の宝物を買いに行くときとあまり変わりません。自動取引の焦点は、戦略的ステータスの管理にあります。

SQL などのリレーショナル データベースにまだ慣れていない場合は、Redis などの NoSQL インメモリ データベースの使用方法を直接学習することをお勧めします。その利点は、簡単に開始できること、優れたパフォーマンスを備えていること、および本質的にシングルスレッドであることです。読み取りおよび書き込み時のデータのロックなどの低レベルの操作を考慮する必要がありません。ただし、主キーが存在せず、番号の自動インクリメントなどの機能が実現できないのが欠点です。必要に応じて、コードを記述して自分で実装する必要があります。しかし、私は自動売買でこの機能をあまり長い間使ったことがありませんでした。

もう 1 つの重要ではない問題は、インメモリ データベースの場合、保存する必要があるデータの量が比較的大きい場合、サーバーのメモリを大きくする必要があることです。ただし、一般的な定量取引の場合は、4G または 2G のメモリで十分です。それほど多くのデータを保存する必要はありません。

Redis は現実世界では非常に強力です。戦略に高頻度の要件がある場合、パブリッシュ/サブスクライブ メッセージ サブスクリプション モデルも実装できるため、さまざまな MQ モジュールを追加する必要がなくなります。前述したように、MySQL のようなリレーショナル データベースを使用する場合、これまで実際に使用したことがない限り、比較的嫌で難しいプログラミング言語である SQL の学習に時間とエネルギーを投資する必要があります。誰もがクオンツ取引をするためにここにいるのであり、コードの書き方を学ぶためにここにいるのではないことに注意してください。

また、パフォーマンスを考慮してマーケットセンターを構築したい場合は、利便性を考慮して Redis データベースも使用し、メッセージキューと連携します。ソリューションはシンプルで、ほとんどのシナリオに適しています。 1 つのサーバー (または複数のサーバー) を使用できます。

コード設計

基本的に、コードを記述するときは、コアとなるデータ構造が最も重要です。データ構造の設計が合理的ではなく、さまざまなモジュールが結合することになるため、コードがぎこちなく書かれ、修正が非常に面倒になります。しかし、それを適切に設計するには、ある程度の取引経験、つまりビジネス経験が必要です。これらはすべてコードの書き方に関するものであり、言うことはあまりありません。戦略は人それぞれ異なり、平等に扱うことはできません。ただし、いくつかの原則はおそらく同じです。

たとえば、注文が送信された後は、それが成行注文であっても、取引所によって拒否される可能性があるため、成功する保証はありません。たとえば、交換がビジーすぎるか、一時的なネットワーク データ パケットの損失が発生します。次に、2 フェーズ コミットに似たいくつかの中間状態を設計するのが最善です。失敗した場合は、試行を続けます (もちろん、あまり頻繁に試行しないでください。そうしないと、制限を超えると禁止されます。これを行うための特別な再試行デコレータがあります)。 、またはエラー状態を修正するメソッドを追加します。

通常であれば、取引所がダウンしても短時間で回復します。回復しないと、取引所または自分のコード サーバーのどちらかが完全にダウンしてしまいます。これは監視し、警告する必要があります。自動コードですべてをカバーすることは不可能です。自動で処理できない場合は、手動による介入が必要になります。手動処理​​が時間内に処理されなかったとしても、エクスポージャを増やすためにポジションをオープンし続けない限り、問題はそれほど大きくはなりません。底値をカバーするためのアルゴリズムによるストップロス注文があるからです。

以下は私が自分で整理した戦略開始ロジックです。

基本的に、同様のフローチャートを描くことで、コードがより整理され、潜在的なバグの数が減ります。そのため、コードを書く前にフローチャートを書いたり、書きながら整理したりすると、ロジックが明確になり、アイデアが湧いてきます。

ポジションを閉じるロジックはこれよりもはるかに複雑です。これは、ポジションを閉じるには、ストッププロフィット、ストップロス、ポジションのクローズをトリガーするさまざまな戦略のエグジットシグナルなど、さらに多くの条件があるためです。つまり、エントリーよりもイグジットの重要性が高く、実際のオファーも少し面倒です。

戦略ロジックは人それぞれ異なるため、ここでは詳しく説明しません。重要なことは、戦略ロジックを修正し、起こり得るさまざまなエラーの発生を防ぐことです。

ただし、これらは複雑に見えますし、単なる if else の集まりです (Python には switch ステートメントさえありません)。条件が満たされている場合は、次のステップに進みます。そうでない場合は、大きな問題はありません。

時間制御

取引を行う場合、タイミングは間違いなく非常に重要です。ここでは、私の時間管理スキルをいくつか紹介します。

一般的なプログラミングでは、定期的に実行する必要があるタスクがある場合、apschedulerなどのcronジョブパッケージを使用することが一般的です。たとえば、時間になるとすぐに K ライン価格でポジションをオープンする必要があるかクローズする必要があるかを検出するスケジュールされたタスクを計画します。

この種のパッケージは、実際には、設定された時刻 (第 2 レベルまで正確である可能性があります) にすぐに新しいスレッドを開き、指定されたタスクを実行します。問題は、このタスクの実行時間が長すぎると、次のタスクが実行されることです。問題は解決できますが、さらに厄介になります。

私は通常、ログ情報を定期的に出力したり、プログラムの自己修正や自己テストを実行したりする必要がある場合に、このようなスケジュールされたタスクを使用します。これらの機能は、頻度が低くすぐに完了できる機能です。これらの関数はデータベース内のデータを読み取るだけであり、書き込み操作や更新操作は行わないため、apscheduler に別のスレッドを開いて処理させても問題ありません。

ただし、市場価格やローカル ポリシーのステータスの継続的かつ頻繁なクエリを必要とするロジックは、このようなタイミング パッケージには適しておらず、while ループで制御する必要があります。

たとえば、戦略で K ラインのバー内のシグナルを使用する場合、シグナルがいつでも現れる可能性があることを意味し、原価格または取引量を常にチェックして、対応する操作を実行する必要があるかどうかを判断する必要があります。たとえば、現時点でより正確な時間制御が必要な場合は、k ライン時間の終了 10 秒前にポジションをオープンする一連のロジックのみを実行します。では、最後の 10 秒かどうかを簡単に判断するにはどうすればよいでしょうか?

タイムスタンプを使用して 3600 の残りを取得しました。

(コンピュータのタイムスタンプ、つまりタイムスタンプに詳しくない方のために説明します。タイムスタンプはグリニッジ標準時 1970.1.1 から始まります。たとえば、1691240298 秒あたりです。コンピュータが正確な時間であれば問題ありません)マイクロ秒レベル)

3600 から余りを取り出した後、余りが 3590 秒を超える場合、それは最後の 10 秒です。誤差はミリ秒レベルにまで及ぶ可能性があります。前述したように、主要なトランザクション操作は 1 つのスレッドで順次実行するのが最適です。この方法では、実行時間を大まかに制御したり、期間をぼかしたりできますが、問題はありません。

このような制御を行うには、自サーバーの時刻設定が標準時刻から大きく乖離していないことが必要です。ただし、この種の制御はあまり正確ではなく、第 2 レベルまでしか到達できません。ただし、Python は時間をあまり正確に制御できないため、スリープ関数ではマイクロ秒程度の誤差が発生します。

APIアクセス頻度を減らす

Web ソケットに基づく独立したマーケット センターを採用して、異なる戦略間または異なる取引コード間で K ラインなどの市場公開情報を共有している場合、この点で API の頻度制限を心配する必要はありません。

ただし、口座情報、つまりファンド、ポジション、注文の3つの情報をタイムリーに取得する必要もあります。アカウント情報は WebSocket を使用して、Exchange サーバーがアクティブにプッシュするのを待つこともできますが、3 つの理由により、Rest API を直接使用する方が簡単です。

まず、WebSocket によってプッシュされるアカウント情報の負担を軽減するために、取引所は時々再接続するか listenKey を更新する必要があります (Binance は 60 分)。この状況を維持するのは明らかに面倒です。それほど面倒ではありませんが、API ホスティングを行って複数の口座を同時に取引する場合、情報のリアルタイムの正確性を確保するために別のプログラムを有効にする必要があり、ロジックは非常に複雑になります。

第二に、一部の取引所には口座情報更新用の WebSocket プッシュがありません。この場合、取引ロジックは普遍的ではなく、異なる取引所間で実際の取引コードを切り替えるのはさらに面倒です。もちろん、ほとんどの人は複数の取引所で取引することはなく、そのような状況に遭遇することはありません。

3 番目に、WebSocket リンクが壊れている場合、または前述の listenKey の更新や有効期限などの問題が発生した場合、プログラムが時間内にそれを検出できず、その時点でトランザクションが発生した場合、WebSocket のみに依存すると、見逃したアカウント更新情報が再度プッシュされないため、比較に大きな問題が発生します。このような潜在的な問題の可能性を回避するには、残りの API を利用して特定のアカウント情報を取得する必要もあります。結局はrest apiに頼らざるを得なくなるのですが、それは不要ではないでしょうか?

要約すると、中頻度および低頻度のトランザクションの場合は、Rest API を直接使用してアカウント情報を取得する方が良いでしょう。重要なデータ ソースについては、SSOT (Single Source Of Truth) に従うのが最善です。そうしないと、潜在的な混乱を引き起こす可能性があります。この概念は自分で Google で調べることができ、データ処理に依存するプログラミングに非常に役立ちます。

次に、アカウント情報を取得するために REST API のみを使用する場合、ローカル コード メモリに一時的なアカウント情報テーブル、特に利用可能な資金を維持できます。これにより、取引所への頻繁なアクセスを減らすことができます。すべての注文を一度に送信することができます。注文をバッチで発注し、更新の背後にあるロジックをゆっくりと処理するのと同様に、注文が速くなればなるほど、スリッページは自然に小さくなります。

さらに、アルゴリズムによるストップロス注文が約定したかどうかを判断するには、未決注文のステータスを直接取得する代わりに、最近の高値と安値を使用できます。これは、注文が約定したかどうかを判断するには、ポーリングする必要があるためです。これは比較的頻繁に行われるため、無効な API アクセスが大量に発生します。 WebSocket の K-line 価格更新は非常にタイムリーであり、API の頻度制限を占有しません。

同様に、いくつかのエグジット命令もこのようになる可能性があります。イグジットシグナルがバー内の価格に依存している場合は、最近の高値と安値を使用する方がよい場合があります。価格がトリガーされてすぐに戻る場合もあるため、チェック頻度があまり高くないと見逃してしまう可能性があります。取引戦略の一貫性を確保するには、価格を見逃さないように高値と安値を確認してください。このアプローチには、実際の取引価格が実際のトリガー価格より有利になる可能性があるという追加の利点があります。

上記2点がわかりやすいかわかりませんが。実生活での経験が必要になる場合があります。

皆さんのお役に立てればと思い、実際のコード開発における私の経験の一部を共有したいと思います。