IB証券TWSのAPIドミトリーFAQ:実装メモ
こちらはIB証券(インタラクティブブローカーズ証券)のAPI情報で、公式以上に細かい点がカバーされているDmitry's TWS API FAQを日本語翻訳にかけつつ、わかりにくい部分をできる限り修正した上で魚拓保存しているものです。元のウェブページは量が多すぎて閲覧しにくく、元サイトを翻訳にかけるとPCが固まる上、最近アクセスが不安定になっているため、備忘録用に章ごとに分けて保存しています。
全体目次
実装メモ(このページ)
ContractDetails、Index、注文方法(共通)
コンボ注文、ブラケット注文、株式・オプション・先物注文
約定照会、デモ口座とリアル口座、グラフ化
データサブスクリプション
スキャナ、基本仕様、過去データ制約、IBゲートウェイ
FX関連、ポートフォリオ情報、その他
実装メモ
[Q] オプションレッグのビッド/アスクを取得するにはどうすればよいですか?
[A] 今のところ3つの方法が見つかりました。私が実際に試してみたのは 1 つだけですが、ここでは 3 つすべてについて説明します。
1-of-3: client.reqTickByTickData の使用(19003、ContractSamples.USStockAtSmart()、"BidAsk"、0、true);
この記事を読む: TWS API v9.72+: ティックごとのデータ
特に、Bid(売値)とAsk(買値)の受け渡し機能を提供するIBApi::EWrapper::tickByTickBidAskについて読んでください。
ここでの問題は、同時サブスクリプションが 3 つしか許可されていないことです。これは、1 ~ 2 つの問い合わせを走らせている人には機能するかもしれませんが、同時により多くのコントラクトのBid/Askを取得するためのより一般的な方法には適していません。
2-of-3: EClient.reqMarketDepth (Java、C++、Python では reqMktDepth) を使用します。次の 2 つのドキュメントを参照してください。
TWS API v9.72+: マーケットデプス (レベル II)
TWS API v9.72+: EClient クラス リファレンス
IB はドキュメントの中で次のように述べています。
そして、常に特定の取引所でのみ取引される契約を取引している場合を除き、この「集約データ」が適切なBid/Askを取得する唯一の正しい方法であるという結論に達しました。したがって、特定の取引所からデータを集約する必要はありません。
Contract.Exchange を SMART に設定し、「isSmartDepth」引数を true に設定して「reqMarketDepth」を使用すると、さらにサブスクリプションが必要であるというエラーが発生します。私のデータサブスクリプションには、「IEX交換」のための無料のものを除いて、レベルIIはありませんでした。
Contract.Exchange = "IEX" を設定し、"isSmartDepth" 引数を false に設定した場合、データの取得は開始されますが、たとえば "IBM" 銘柄で低ボラティリティの日でもBid/Askが変動することに気付きました ( TWS では約 $0.02 と表示されます)。多くの場合、IB API によってはるかに大きな数字として報告されます。IB サポートは、TWS Bid/Askで表示されるもの (さまざまな取引所のリスト付き) は、サブスクリプションなしでは API 経由でアクセスできないが、TWS ウィンドウでは「人間の肉眼」で無料でアクセスできることを確認しました (OCR を使用する時が来ました!)。
例えば、2021 年 2 月 8 日午後 2 時 47 分頃、IB API が 2.45 ドルを返し、TWS がBid/Ask = 0.02 ドルの集計データを表示したタイミングで、IEX ビッドが 121.0、IEX アスクが 123.45 でした。したがって、「IEXバージョン」によるスプレッドのみ 2.45になります。役に立たない!
「reqTickByTickData」に SMART を使用したい場合は、この契約が取引されるすべての取引所のレベル II サブスクリプションが必要になります。
すべての金額を確認するには、 interactivebrokers.comにログイン -> アカウント管理 -> 設定 -> ユーザー設定 -> マーケット データ サブスクリプション ->「北米: IEX Book Depth – トレーダーワークステーション – 手数料免除」が表示されます。IEX Exchange Booking Service Depth、NYSE (Network A/CTA)、AMEX (Network B/CTA)、NASDAQ (Network C/UTP)、または米国株式およびオプションのアドオンストリーミングバンドルへの事前のサブスクリプションが必要です。
次に、[現在の GFIS サブスクリプション] -> [歯車アイコンの設定] -> [北米] (例) -> [Level II (Deep Book)] をクリックします。そして、価格のリストが表示されます。
3-of-3: リアルタイムのバーと「ティック」を使用した買値
次のいずれかの方法で 2 つのリアルタイム 5 秒足ストリームを購読する場合:
a) 「keepUpToDate」を true に設定したIBApi.EClient.reqHistoricalData リクエスト( 「TWS API v9.72+: 履歴バーデータ」も参照)
または
b) IBApi.EClient.reqRealTimeBarsリクエスト ( 「TWS API v9.72+: 履歴バーデータ」も参照)
そして、「表示対象」を 1 つのリクエストでは「BID」に、別のリクエストでは「ASK」に指定すると、IB API は数秒ごと (平均 3.5 秒ごと) に 5 秒のバー更新と各ストリームの「バー」でコールバックします。 「close」価格は最新の売値と買値を表します。
唯一の問題は、これら 2 つのストリームが 2 つのイベントストリーム (同タイミングで同期されていない) を表していることです。bid - askスプレッドを計算してマイナスになってしまったものを除外し、入札更新と売値更新が500ミリ秒以内の距離にある場合にのみbid - askスプレッドを使うなどの対策が必要です。その結果、同じ「IBM」ティッカーで(取引日全体を通して)平均して3.5秒ごとに計算されたbid - askのスプレッド値が得られました。これで私にとっては十分です 🙂
注 #1: この方法は、利用可能な 100 回線のうちの「1 回線」としてのみカウントされるため、理論的には、データブースターやレベルをサブスクリプションせずに 100 個の bid_ask スプレッド (価格更新用にサンプリングされた「リアルタイムティック」を含む) を取得できます。
注 #2: 「表示対象」のタイプには「BID_ASK」があります (「履歴データのタイプ」を参照) が、現在の最新の売値と買値の即時スナップショットは提供されません。代わりに、次のものが得られます。
bar.open = "平均入札時間"、bar.high = "最大売値" (役に立たない)、bar.low = "最小買値" (役に立たない)、および bar.close = "平均買値時間"。5 秒足の「時間平均」値が上記の方法よりも優れているかどうかはわかりません。突き合わせて確認してみますが、私の推測では、どちらのアプローチでも同様の品質の出力が得られるでしょう。
この方法が、動きの速い市場にどれだけうまく対応できるかはわかりません。実際の状況についてかなりランダムな推定値が得られると思います。ただし、常に何らかの安全係数を使用できます (たとえば、計算された bid_ask スプレッド値を使用し、念のため 2.0 を乗算します)。また、スプレッド値が現在のボラティリティとどのように相関するかを確認することも興味深いでしょう。ボラティリティの高い時期には、価格が急騰したり、売値/買値が急騰したりする可能性がありますが、高いボラティリティの測定値はほぼ横ばいの高値を示しており、これを使用して売値/買値の最大値を推定する(および/または「セーフティーネット」としての閾値を調整する)ことができます。「係数」を設定し、市場が狂ったときに少し高くします。
私の記憶が正しければ (4 番目のオプションとして) 15 分遅延の無料の bid_ask ストリームがありますが、15 分の遅延は遅すぎるため、そのオプションについては調べていません。
乾杯、
ドミトリー・シェフコプリアス
ps: ここでは、bid_ask 計算のプロトタイプを作成するためにすべての受信バーで使用した関数を示します。この部分を「メタ言語」と考えてください。
public void on_historical_bar_try_to_calculate_bid_ask_spread(
Bar bar, RequestHistoricalData request) {
// Check we have both requests in place (not nulls)
if (request_historical_data_bid == null) {
logger.debug("request_historical_data_bid == null, nothing to do yet…");
return;
}
if (request_historical_data_ask == null) {
logger.debug("request_historical_data_ask == null, nothing to do yet…");
return;
}
// Both bid / ask "real time" bars requests are present! We can calculate bid/ask spread estimate.
// Let's extract most recent bars from 2 streams:
Bar bid_bar = request_historical_data_bid.most_recent_bar_received;
Bar ask_bar = request_historical_data_ask.most_recent_bar_received;
if (bid_bar == null) {
logger.debug("bid_bar == null, nothing to do yet…");
return;
}
if (ask_bar == null) {
logger.debug("ask_bar == null, nothing to do yet…");
return;
}
// Check how far in time they are from each other long
bars_received_time_delta_ms = request_historical_data_bid.most_recent_bar_received_time.get_delta_ms(request_historical_data_ask.most_recent_bar_received_time);
// Filter out updates with more than 500ms distance
if (Math.abs(bars_received_time_delta_ms) > 500) {
return;
}
// Both bid/ask bars are present and close enough, let's calculate the bid/ask spread
double bid_ask_spread = ask_bar.close() – bid_bar.close();
// Filter out some obvious BS
if (bid_ask_spread <= 0) {
return;
}
logger.info("Calculated synthetic poor man's bid_ask_spread: "
+ Aid.math_round(bid_ask_spread, 2)
+ " bars_received_time_delta_ms: " + bars_received_time_delta_ms);
}
}
[Q] OCAグループ注文を閉じるにはどうすればよいですか?
[A]
2021 年 2 月 22 日に追加
Nick: 逆指値注文を成行注文に変更することもできます。これはすべての注文タイプで機能するとは限らないため、自分の環境で実際に試してみる必要があります。
[素敵でエレガントなソリューション!]
ds-avatar: TWS は、OCA (one-cancels-all) グループ ID を子ストップロス注文に自動的に割り当てます。これにより、複数の子注文が送信された場合に 1 つの子注文のみの実行が実装されます。これを手動の市場撤退注文に使用して、その実行によって子注文がキャンセルされるようにすることができます。
[Q] 戦略を特定するための順序フィールドは? (clientID 対 orderRef 対 permID)
同じアカウント/サブアカウントで複数の戦略を実行していますが、どのアカウントがどの注文をしたのかを区別したいです。
Order クラスに、カスタム値を割り当てることができ、その値を永続化して、後で誰が注文したかを識別できるフィールドはありますか?
誰かがorderRef フィールドを使用することを提案しました。
同様の質問: Corneliu Maftuleac (#46716)
注文の orderRef フィールドを使用して、注文が特定の戦略に属していることをマークします。ただし、フレックスクエリを使用して注文のリストを取得する場合、OrderReference フィールドは常に空になります。
ドキュメントに指定されているとおり: 注文チケットでユーザーが定義した注文参照番号。
[Q] 注文参照を取得する方法はありますか?
[A] 2021 年 2 月 22 日に追加
OrderRef がありますが、セッションをまたがって保持されない可能性があるため、自分のやりたいことで正しく動作するか確認する必要があります。
「セッション」について:外国為替を除いて、市場は 24 時間 365 日継続的に取引されるのではなく、1 日の一部だけしか開いていません。一般的な考え方は、市場は特定の時間に開き、その後特定の時間に閉じる、これが 1 つの取引セッションであるということです。翌日の取引は別のセッションとなります。
金融商品が異なれば、セッションの開始と終了に関するルールも異なります。主要な先物取引所の 1 つのセッションは暦日にわたっており、ある日の夕方に始まり、真夜中を過ぎて午後または翌日まで続きます。取引は月曜日の夜に行われる可能性がありますが、火曜日の取引日としてカウントされるため、頭がおかしくなるかもしれません。
OrderRef に戻ると、注文が行われたときと同じセッションで約定する場合はフィールドが返されるが、注文が後続のセッションで約定される場合は空になることが観察されています。
Mark Colling 氏も OrderRef を提案しました。
注文参照フィールドに保存されている取引参照を使用していますが、通常は問題なく機能します。注文 ID、注文参照、内部追跡 ID を追跡すると、すべてをつなぎ合わせることができるはずです。
私が経験したのは、ポジションレポートは何分も遅れており、約定レポートは任意の順序で到着しますが、注文参照は安定しているようです。
ds-avatar による回答:
OrderRef は、最新のセッションのレポートのみに含まれます。回避策: 日次レポートをメールで送信し、収集して照合するように自前のシステムを作ります。
[ベストアンサー by praditik]
permIDを使用し 、それをローカルに保存してマッピングを識別しました。セッションや日をまたいでシームレスに機能します。したがって、他の誰もがすでに述べたように、プログラム内でローカルマッピングでpermID を使用する と、うまくいくはずだと思います。
解決:はい、残念ながら OrderRef は最終的には消えてしまいますが、レポートにはアカウント固有の識別子である order.permID フィールドも含まれており、永久に存在するはずです。
したがって、すべてのordersRef値と、注文に割り当てられた対応するpermIDをローカルで追跡できます。したがって、数か月後、IB からレポートを取得すると、ローカルテーブル (orderRef <–> permId) を検索することによって、元の orderRef 値を復元できるようになります。IB のレポートは独立したものであるべきであり、既存のフィールドの一部を削除する理由がわかりません (存在する場合は、永久に存在するはずです)。しかし、orderRef値が消えてしまうのは事実です。うまくいくかどうか教えてください。レポートに permID が表示されますか?
Corneliu Maftuleac: はい、レポートに permID が表示されます。
[Q] TWS APIの接続が切れた場合の検出方法
[A] 2021 年 2 月 4 日に追加
ユージーン
はい、2015 年 9 月頃に別のスレッドで非常によく似たトピックが議論されました (件名: 非同期 EClient::eConnect() は必要ですか?)。その結論は、TWS API に接続しただけで何も送信しないと、問題が発生する可能性があるというものでした。それを迅速に検出する仕組みが必要です (場合によっては、TWS 側で接続が古くなり、再起動しないと (または 30 ~ 40 分ほどかかると)、同じクライアント ID を使用して TWS に再接続できなくなります)。
これに対する解決策は、定期的にTWS APIに対して問い合わせをすることで、TWS サイトがクライアントアプリへの確立された TCP 接続をキープしようとすると同時に、クライアントから TWS API に単純なもの(reqCurrentTime() など) を要求し続けることになります。 TWS は、リモート IB サーバーに迷惑をかけたり問い合わせたりすることなく、独自に応答します。そのため、煩わしくても安全です😊 これら 2 つの「ストリーム」が、あなたが探している「ハートビート」になります。
それが役に立てば幸い、
ドミトリー
こんにちは、カートさん
午前1時30分の私の返信では、「TWS <==> IB 」ではなく「 client <==> TWS 」検出で接続が落ちたようです。
ユージーンが述べたように、APIクライアントは注文を送信しようとしたとき(またはTWSへの他のAPI呼び出しを実行したとき)にのみ接続損失を検出します。これは、私のクライアントがTWSボックス以外で実行されていたときに上記のスレッドで約1年前に観察した問題とまったく同じです。
したがって、それを防ぐには (そして TWS への接続が失われたことを API クライアントがより早く認識できるようにするには)、クライアントは TWS と通信することを試みる必要があります。また、TWS は、クライアントに何かを配信できなかった場合、内部接続記述テーブルをより迅速にクリアします。そのため、時間を尋ねる (クライアント -> TWS) ことと、ティッカーをサブスクライブする (TWS -> クライアント) ことで、両方向の接続を確保することを提案します。
「 TWS <==> IB 接続が失われました」に関しては、「client <==> TWS 」接続は正常です (簡単にするために、API クライアントと TWS が同じホスト上にあるとします)。 TWS には確かに何らかのメカニズム (ハートビートなど) があります。 TWSで何もサブスクライブしていない場合は、10秒ごとにリモートIBホスト(私の場合はcdc1.ibllc.com、~/Jts/jts.iniに書かれています)が次のようなコンテンツを含むパケットをプッシュしていることがわかります。パケットペイロード: 「FIX.4.1.9=00014.35=1.112=farm.10=217」 および
「FIX.4.1.9=00027.35=0.112=FixTestRequest195.10」
次に、TWS は次のような FIX プロトコル関連の内容を IB に報告します。
「FIXCOMP.9=70.x」
「TWS <==> IB 接続」が失われると、API クライアントは TWS によって直ちに通知されます。したがって、幸いなことに、TWS が IB との接続の認識についてこれまで共有されていなかった情報をクライアントアプリと「共有」するための新しい魔法の杖を発明する必要はありません。このロジックはすでに存在しています。私の TWS は 30 秒以内に接続が失われたと報告し、ネットワークが復元されると 5 ~ 10 秒以内に回復します。
真夜中の発言でわかりにくくてごめんなさい 🙂
ありがとう、
ドミトリー
[Q] 取引所で全シンボルのリストを取得しますか?
[A] 2021 年 1 月 29 日に追加
NASDAQ には API があり、すべてのシンボルとその最終価格のリストを JSON として提供します。
https://api.nasdaq.com/api/screener/stocks?tableonly=true&limit=25&offset=0&download=true
[Q] 株式/オプションの標準偏差を取得しますか?
TWS で簡単なオプション分析グラフを取得する方法はありますか?
株価の標準偏差の計算方法をご存知ですか?
目標は、株式/オプションの SD を計算するために必要なすべてのデータを API 経由で取得することです。
[A] (#44329) 2020 年 10 月 3 日に追加
reqHistoricalData の HISTORICAL_VOLATILITY を見てください。
- また
TWS では、オプションを右クリックしてそのパフォーマンス プロファイルを尋ねることができます。
ID 104 と 106 の株式コントラクトで reqMktData を使用すると、特定の株式の年換算の HV(ヒストリカルボラティリティ)と IV(インプライドボラティリティ)が得られます。ID 106 の特定のオプション契約の年換算 IV を取得できますが、HV は取得できません。
30 ティックを超える場合は、IB がウィンドウ 30 の Garman Klass 式を使用していると思われるため、HV を自分で計算することもできます。しかし、他のボラティリティ式を使用することもできると思います。
もちろん、特定の日付の正確な範囲を取得するには、ボラティリティを期間に合わせて調整する必要があります。15 営業日にわたるボラティリティが必要な場合: VOL / ((15/252) ** 0.5)
お役に立てれば
[Q] APIから切断されたTWSを検出するにはどうすればよいですか?
2020年5月20日水曜日午後2時45分、スプレッド・ヘンドラーは次のように書きました。
私は TWS 978.1 と最新の TWS API 9.79.01 (できれば最新の API バージョン) を使用しています。
TWS が API から切断された場合 (残念なことに、TWS978 が頻繁に突然シャットダウンしてしまいます)、API 経由でエラーメッセージは受け取れません。
[A] by rwk、2020 年 8 月 4 日に追加
何か問題が発生したことを知らせる切断メッセージに依存するのは危険であるように思えます。接続が良好であっても、データを取得できない、または注文できない可能性があります。私がやっているのは、アクティブであるべきだとわかっているインデックス ETF などの契約を監視することです。データがない状態が 20 秒間続くなど、一定の時間が経過した後、アラーム音を鳴らして調査します。
[Q] 「no value」とはどのような状態ですか?
[A] by Kurt
IB では、「値なし」を意味する値の使用に一貫性がありません。INT_MAX または DOUBLE_MAX の場合もあれば、0 の場合もあり、-1 の場合もあり、-0.99 の場合もあります。
ヒストリカルバーボリュームフィールドは TRADES に対してのみ設定され、BID、ASK、MIDPOINT、BID_ASK、OPTION_IMPLIED_VOLATILITY、または HISTORICAL_VOLATILITY に対しては設定されません。したがって、TRADES のデータがない投資対象 (FX など) には利用可能なボリュームがありません。
おそらく、特定のパターンに従わず、文書化されていない方法で、さまざまなデータフィールドで省略されているものが他にもたくさんあるでしょう。得られるものだけを得ることができます。様子がおかしくても心配しないでください。それは、IB で発生する問題の中でもたいしてクリティカルではありません。
過去のデータでは、バー内の未使用のフィールドが 0 になる場合もあれば、-1 になる場合もあることに確かに気づきました。確かにそれに関する文書はありません。
–カート
[Q] ID / nextValidId を処理するためのベスト プラクティスは何ですか?
[A] edbar@exmsft より
ID をハードドライブに保存します。
次に、TWS を起動するときに、TWS が送信する nextID をチェックして、保存したものと比較します。保存されている ID が TWS 番号より大きい場合は、自分の ID を使用します。その後、ID 番号を増やすたびに、ハードドライブのバックアップを更新します。
いつでも TWS CONFIG API 画面に移動して、API ID 番号をリセットできます。
–エド
[A2] btw12342001 (同じスレッド)
orderIdをtickerIdに使用するべきではないと思います。reqAnythingData に指定する番号は orderId ではなく、コールバックでどのようなデータが来るかを知るためのものです。複数のデータシンボルをサブスクライブする可能性があります。orderId と混同される理由はまだ説明されていません。
[A3] by Kurt (同じスレッド)
ここで多くの人が nextValidId() を使用するだけでは機能しないと報告しています。問題なく動いていたので覚えていませんが、(ディスク上に)記録しておくのが一般的だと思います。最大の注文 ID を使用し、その値と返された値の大きい方を使用します。起動時に nextValidId を1つ増やし、そこからインクリメントさせていきます。
reqMktData などに関しては、それぞれに独自の ID スペースがありますが、おそらくそれらを一意に保ちたいと思うでしょう。私はすべてのリクエストIDを保持しています。一意のリクエスト ID により、受信リクエストエラーのチェックが容易になります。
-カート
[A4] リチャード L キング著 (#44680)
2020 年 10 月 10 日に追加
API によって、ID を使用するさまざまなリクエストに対して異なる範囲の ID を使用するという要件はありません (唯一のルールは、注文 ID を絶対に減少させてはいけないということです。ただし、TWS 経由で特定の clientID の注文 ID をリセットできる点は異なります)。さらに、市場データのリクエスト、履歴データのリクエスト、注文などに同時に同じ ID を使用することを妨げるものはありません。
ただご想像の通り、同じIDを使いまわすとコールバックエラー時に潜在的な問題が発生します。これに対処する 1 つの方法は、独自の範囲を定義することです。たとえば、市場データ リクエストの場合は 0 ~ 1023、契約詳細リクエストの場合は 1024 ~ 2047 などです。
もちろん、範囲の終わりに達したときに何が起こるかを考えて対策を入れる必要があります。これに対処する方法はたくさんあります。そのような考慮事項は、範囲をどれくらいの大きさにするかを決定する際の指針となります。
たとえば、契約の詳細リクエストはかなり早く完了するため、ほぼ確実に、たとえば 1000 個の ID の範囲で問題を回避し、最後に到達したら最初に戻るだけで済む可能性があります。最初のリクエストが存在しない可能性がありますし、次の 999 件のリクエストを行うまでに完了するという前提に立っています。ただし、私は個人的にはリスクを負いません。使用された ID を追跡するために、データ構造を実装する必要があります。
これを行うと、プラス値の ID を含むエラーメッセージについて、そのメッセージがどのタイプのリクエストに関連しているか、さらには、たとえばテーブルのインデックス付けに ID を使用する場合など、正確にどのリクエストであるかを判断するのが簡単になります。
リクエストタイプごとにこのような仕組みを使っています。特に、注文 ID には 0x10000000 から 0x7FFFFFFF を使用します。この範囲が広いということは、同セッション中に注文 ID が不足することはない (注文 ID は 1,879,048,191 個あります!) ため、注文 ID のリセットメカニズムを使用する必要がありません。また、この範囲内で、単一注文とエントリー注文の ID が 0 または 5 で終わり、括弧内のストップロス注文が 1 または 6 で終わり、括弧内のテイクプロフィット注文が 2 または 7 で終わることを確認します。これは次のとおりです。ログファイルを確認するときに注文の役割を識別しやすくするためです。
この種のスキームは、CompletableFutures の使用と完全に互換性がある必要があります。
リチャード
[Q] 接続が失われた後、同期するためにすべてのオープン注文とポジションのリストが必要ですか?
[A] by src
reqAccountUpdates() メソッドを呼び出すと、ポジション情報が updatePortfolio() イベントで報告されます。イベントはポジション情報ごとに 1 回発生します。その後、変化がある場合は更新され、変化がない場合は約 2 分に 1 回更新します。
[rwk]
いったん接続切断要求をすることも検討してみてはいかがでしょうか。場合によっては注文を出したり、実行レポートをリクエストしたり (またはその両方) するのもいいです。ポジションも良いアイデアです。
-カート
> reqAccountUpdates() の acctCode パラメータで何を送信すればよいですか?
FA アカウントでシナリオがない場合は、null を設定します。
-カート
または、アカウント コード UXXXXXX または FXXXXXXに設定できます。
> updatePortfolio() のposition パラメータには何を受け取りますか?
ポートフォリオの契約数ですか?
はい
[Q] 追跡時間(内部処理)
[A]ドミトリー著
これは、外部から受信するタイムスタンプとは何の関係もありません。すべての内部イベントのローカルタイムスタンプのみを目的としています。十分な単位として、ミリ秒を選択します。ローカル時刻は、できる限り頻繁に NTP サーバーからポーリングされた更新によって NTP で維持されます (GPS pps 信号に基づいて独自の Stratum-1 NTP サーバーを構築する必要がありました。
long get_epoch_ms()
{
// http://brian.pontarelli.com/2009/01/05/getting-the-current-system-time-in-milliseconds-with-c/
timeval t;
gettimeofday(&t, NULL);
long epoch_ms = (t.tv_sec * 1000) + (t.tv_usec / 1000);
//std::cout << ";" << t.tv_sec << ";" << t.tv_usec << " or simply epoch_ms: " << epoch_ms << std::endl;
return epoch_ms;
}
[Q] ライブトレードの構築
関連項目も参照してください: [Q] ある程度正確な累積量を取得するにはどうすればよいですか?
関連項目も参照してください: [Q] NickSize() NickPrice イベントのシーケンスは何ですか?
[A]ドミトリー著
成立した取引のイベントを抽出したい場合 (一部の BID/ASK変化ではなく、実際に成立した実際の価格/出来高の取引)、IB は相対的なものしか送信しないため、完全に単純ではない可能性があるため、次の考慮事項に従う必要があります。取引に関する絶対的な自己完結型の個別メッセージの代わりに「違い」を表示します。ウォームアップするには、以下をお読みください: [Q] lastSize=0 の場合があります。なんと?
ここで、Dmitry が使用する取引を取得するための実装アルゴリズムは次のとおりです。
– MyContract クラス内に取引用のコンテナがあります
– すべての tinyPrice()、tickSize() イベントは、ewrapper によってリクエスト オブジェクト (RequestMktData) にルーティングされます。このリクエスト オブジェクトは MyContract クラス インスタンスに関連付けられており、このインスタンスには、受信ティックを処理するための tinyPrice()、tickSize() メソッドがあります。
– 次に、MyContract クラス内で次のようにします。
void MyContract::tickPrice(Account *account, long epoch_ms, TickerId tickerId, TickType tickType, double price, int canAutoExecute) {
LOG(INFO) << "MyContract::tickPrice";
// forward raw tick to subscribed algos (if any)
for (std::vector<AlgoBase*>::iterator ialgo = subscribed_algos.begin(); ialgo != subscribed_algos.end(); ialgo++) {
(*ialgo)->tickPrice(account, this, epoch_ms, tickerId, tickType, price);
}
// analyze if GotTrade()
if (tickType == LAST) {
// we got new trade, but we have to wait for TickSize() call to
// complete the Trade obj (and then will call GotTrade(trade) to pass
// complete Trade object to all subscribers)
this->tradesPerAccountMap[account_as_key].push_back(new Trade(account, epoch_ms, tickerId, price));
}
. . .
}
void MyContract::tickSize(Account *account, long epoch_ms, TickerId tickerId, TickType tickType, int size) {
LOG(INFO) << "MyContract::tickPrice";
// forward raw tick to all subscribers
for (std::vector<AlgoBase*>::iterator ialgo = subscribed_algos.begin(); ialgo != subscribed_algos.end(); ialgo++) {
(*ialgo)->tickSize(account, this, epoch_ms, tickerId, tickType, size); // forward tickSize() event to subscribed algos (if any)
}
// analyze if GotTrade()
if (tickType == LAST_SIZE) {
. . .
// CASE 1 of 3: check if last_trade is in "waiting for size" state (last_trade->size < 0 aka 'uninitialized'))
if (last_trade->size < 0) {
// last_trade's size is not yet initialized, means we waited for this tickSize() event
// to complete the trade obj (and forward it to all subscribers)
last_trade->size = size;
last_trade->completion_time_ms = epoch_ms – last_trade->epoch_ms; // we track how long it took us to complete this trade object
last_trade->is_complete = true;
// forward complete trade to all subscribers
gotTrade(last_trade);
return;
}
// CASE 2 of 3: Is it duplicate of last_size tick (same size as last complete trade and within 9ms) => just ignore it
if (last_trade->expect_dup_ticksize && last_trade->size == size && (epoch_ms – last_trade->epoch_ms) <= 9) {
last_trade->expect_dup_ticksize = false; // mark last_trade as "got expected duplicate" so next tickSize if
// happen to arrive within <7-8ms with the same size field would not be treated as duplicate anymore
// (the 'expect_dup_ticksize' flag allows us to ignore tickSize only once
// and only for expected ticks (ones that go after "Last" 2 times in a row))
return;
}
// CASE 3 of 3: It is normal LAST_SIZE tick telling us about new trade with the same price as before! We got new trade!
Trade * new_trade = new Trade(account, epoch_ms, tickerId, last_trade->price, size, true, 0, false);
ptrades->push_back(new_trade);
// forward complete trade to all subscribers
gotTrade(new_trade);
return;
}
…
}
[Q] ライブバーの構築
[A]
https://groups.io/g/twsapi/topic/4046604
https://groups.io/g/twsapi/topic/4046154
[Q] なぜ 2 つの同一の checkSize() コールバックが取得されるのですか?
[A] Richard L Kingによってさらに詳しく説明されています(ここにあります)
2015 年 6 月 9 日に追加
ここで何が起こっているかを要約しましょう:
1. 価格が変更されると、TWS は価格とサイズの両方を含む 1 つのメッセージをクライアント アプリケーションに送信します。
2. IB の API コードは、個別の tinyPrice コールバックと TickSize コールバックを作成します (後者は前者の直後にあることに注意してください。これは API コードで簡単に確認できます。
3. その後、IB のサーバーはサイズを再度送信し、API は別の同一の TickSize コールバックを実行します (この理由は、6 月 2 日の rholowczak の投稿にリンクされている FAQ への私の寄稿で説明されています。これは青で強調表示されている部分です:それをよく読んでください)。
4. 同じ価格で異なるサイズの取引があるたびに、別のtickSizeコールバックが作成されます。
5. 同じ価格と同じサイズで取引がある場合、tickSizeコールバックは行われません。
このことから、tickPrice コールバックごとに少なくとも 2 つの tinySize コールバックが存在することがすぐに推測できます。価格は取引ごとに変わらないため、通常はさらに多くなります。
ちなみに、前回の投稿で言及した「ブランクタイム」をいじる必要はありません。tinyPrice コールバックを受け取った場合は、価格を記録するだけです。次のコールバックは 常にティックサイズ (API コードをチェックして、これが正しいことを確認してください) なので、サイズを記録し、アプリケーションコードを呼び出して、価格とサイズの合計を処理します。その後、tickSizeコールバックを取得するたびに、サイズが最後に記録したサイズと同じである場合は、それを無視します (上記 3 で説明した重複した tinySize になるため)。それ以外の場合は、アプリケーション コードを呼び出して、価格とサイズの合計を処理します。ここで、価格は最新のtickPriceコールバックに記録された価格になります。
上記の 5. で述べたことにもかかわらず、サイズが前のサイズと同じで価格が変更されていない場合に、tickSize が稀に発生する場合があります。データ フィードのおおよその性質を考慮すると、それらを検出するためにコードを複雑にするほどの価値はありません。私自身の API コードは対応するようにいじりましたが、それによってどれだけのメリットがあったのかはわかりません。したがって、誰かが本当に興味を持っていない限り、私はこれらの余分なシワについてわざわざ説明しません。
[Q] 単一のアカウントで 100 を超えるシンボルの市場データを受信するための優れたアルゴリズムを誰かが提案してもらえますか?
[A] ジェイソン・サップ著
私はペーパートレード(デモアカウント)で「ラウンドロビン」タイプのアルゴリズムを使用しています。デモアカウントを使用して、約 1000 個のシンボルのデータを取得しています。私のやり方は次のとおりです。
最初の 90 シンボルに対して reqMktData (累積ボリュームを含む) を呼び出します。reqMktData を 1 秒あたり 30 回以上呼び出さないようにしてください。少なくとも 30 が上限と言われていたと思いますが、この制限はもうなくなるかもしれません。
10 秒待ちます (90 シンボルの最後のシンボルに対して reqMktData を呼び出した後)。
これらのシンボルすべてに対して cancelMktData を呼び出します。
リストから次の 90 個のシンボルを取得します。
最初からやり直してください。
私はペーパートレードでTWSを使用しています。
ジェイソン
———————- 代替案も ———————————
Yahoo Financial の CSV フィードを使用しています。1 つのファイル内で複数のシンボルをリクエストできます。単一のリクエストで各シンボルの価格データのスナップショットを迅速に取得できます。
たとえば、最後の価格、毎日の高値、毎日の安値、終値、出来高をリクエストする場合、2000 個のシンボルがあると、Yahoo からその情報を取得するのに 10 秒ほどかかります。
私は特に朝、株式をスキャンするときにこれを使用します。自分のやり方でラウンドロビンを試みるよりも効率的、迅速、信頼性が高いです。
… ブラント・ハーン著
[Q] リクエスト/レスポンス/エラーのアーキテクチャについての考え方は?
[A] by Kurt
それを行う方法は何百もあります。私は「理想」を信じていません。あなただけでなく他の人にとっても役立つかもしれないので、ここにコードを投稿します。(このコードから、私が使用しているリクエスト/レスポンス/エラー アーキテクチャの大部分を再現できるでしょう。)
リクエストごとにインスタンスを使用してリクエスト追跡クラスを実装しました。デフォルトの動作では、コンストラクターがリクエストを送信しますが、リクエストを作成して送信を延期することもできます。このクラスは、レイテンシーの追跡、ログ記録、リクエストに対する応答とエラーの照合などの便利な機能を多数提供します。
この目的のために、ContractBaseクラスのサブクラス ActiveRequestWithContract を持つ ActiveRequest クラスを用意し、ActiveRequest によって提供される汎用ログ機能に追加情報を追加します。次に、ActiveRequest または ActiveRequestWithContract のサブクラスのいずれかである、IBリクエストタイプごとに個別のサブクラスをさらに用意します。このレベルでは、適切な TWS API 要求メンバー関数を呼び出して要求を送信およびキャンセルするための実装が提供されます。
私は、目的ごとに、または Objective-C ハンドラーへのディスパッチなど、個別のインターフェイス要件ごとにさらにサブクラス化を使用します。
応答とエラーを照合するために、私は既存の要求追跡オブジェクト、つまり削除されていないオブジェクトのリストを保持します。これは、オブジェクトが送信されたか、応答を受信したか、または応答の完全なセットなどを受信したかどうかとは独立して考慮されます。リクエストの最終クライアントは、該当する場合、レスポンスが完了したときにリクエスト オブジェクトを削除するかどうかを決定できます。
その結果、リクエスト ID で検索できる既存のリクエストのリストが作成されます。これにより、リクエスト ID を参照する着信応答と着信エラーを、適切なリクエスト追跡オブジェクトにルーティングできるようになります。したがって、エラーによりリクエストが中止された場合、リクエストクライアントに通知できます。
これは最適ではないため、おそらく不十分であり、確かに理想的ではありません。パフォーマンスが問題になる場合は、リクエストのリストを B ツリー、またはハッシュ テーブルにすることもできます。したがって、C++ STL によって提供される連想配列を使用できます。
いずれの場合も、リクエスト追跡実装の大部分は、tickPriceコールバックから完全に削除されます。ただし、特定の IBリクエストタイプに固有のリクエスト追跡サブクラスごとに、対応する EWrapperメンバーと同じ名前とパラメータを持つメソッド (メンバー関数) があります。関数プロトタイプをコピー&ペーストするだけです。したがって、クラス階層の一部は次のようになります。
MarketDataRequest : ActiveRequestWithContract : ActiveRequest
そして、私のEWrapperサブクラスにはもちろんこのメンバーがあります
virtual void tinyPrice( TickerIdtickerId, TickType field, double price, int canAutoExecute);
私のMarketDataRequestクラスにもあります
virtual void tinyPrice( TickerIdtickerId, TickType field, double price, int canAutoExecute) = 0;
実装は次のレベルのサブクラスに任せられます。このアプローチにより、アプリの詳細が一切含まれていない TWS API への汎用インターフェイスが得られます。
次に、汎用の tinyPrice実装は、二重リンクリストの線形検索を実行して、ルーティング先のリクエスト オブジェクトを見つけます。これは次のようになります。MyWrapper のスーパークラスは、リクエストをログに記録するだけのデフォルト実装を提供します。その結果、この実装では、ルーティングに失敗したリクエストがログに自動的に表示されます。
void MyWrapper::tickPrice( TickerId tickerId, TickType field, double price, int canAutoExecute)
{
for (ActiveRequest *req = marketDataReqTracker.Next(); req != &marketDataReqTracker; req = req->Next())
{
if (req ->ReqId() == tickerId)
{
if (MarketDataRequest *specificReq = dynamic_cast<MarketDataRequest *> (req))
{
specificReq ->tickPrice (tickerId, field, price, canAutoExecute);
return;
}
}
}
inherited::tickPrice (tickerId, field, price, canAutoExecute); // default logging used when response not routed above
}
これは完全に効率的というわけではありませんが、私の目的には「完全に適切」であり、コンテナー抽象化 (STL など) を使用していない場合でも、必要に応じてアップグレードするのが十分簡単です。
MarketDataReqTracker にはMarketDataRequestオブジェクト のみが確実に含まれる必要があるため、dynamic_castの使用は厳密には不要です。したがって、このコードはテンプレートの使用によって恩恵を受けた可能性があります。しかし、それがなくても static_cast は安全であり、パフォーマンスへの影響が問題にならないという理由だけで、私は保守的なことをしただけです。
ただし、ダウンキャストが必要なのは、tickPrice メンバーがMarketDataRequestサブクラスに固有であるためです。つまり、上記の説明によれば、EWrapper インターフェイス全体が各リクエスト オブジェクトに確実に含まれていないためです。
一方、エラー処理はこれにある程度似ており、私の EWrapper サブクラスがこの実装を提供しています。
void MyWrapper::error(const int id, const int errorCode, const CString errorString)
{
(void) error_Route (id, errorCode, errorString);
}
error_Routeは、より賢明で直接的なルーティングのために一連のエラー コードを特別に処理し、それ以外の場合は ReqTracker_error_Route を呼び出します。
ActiveRequest *MyWrapper::error_Route(const int id, const int errorCode, const CString& errorString)
{
if (errorCode == 505)
// actual: "request 2104 error 505 Fatal Error: Unknown message id."
printf ("generic error %d %s [id=%d]\n", errorCode, errorString.CStr(), id);
else if (id == kIBNullReqId)
printf ("generic error %d %s\n", errorCode, errorString.CStr());
else
printf ("request %d error %d %s\n", id, errorCode, errorString.CStr());
bool handled = false;
ActiveRequest *handledBy = NULL;
switch (errorCode)
{
// errors specific to Orders
case 103: // [from doc] Duplicate order ID.
case 104: // [from doc] Can't modify a filled order.
case 105: // [from doc] request 45 error 105 Order being modified does not match original order
case 106: // [from doc] Can't transmit order ID:
…
case 121: // [from doc] Invalid BD flag for the order. Check "Destination" and "BD" flag.
case 122: // [from doc] No request tag has been found for order:
// 123, 124 vague
case 125: // [from doc] Buy price must be the same as the best asking price.
case 126: // [from doc] Sell price must be the same as the best bidding price.
case 129: // [from doc] VWAP orders must be submitted at least three minutes before the start time.
case 131: // [from doc] The sweep-to-fill flag and display size are only valid for US stocks routed through SMART, and will be ignored.
case 132: // [from doc] This order cannot be transmitted without a clearing account.
case 133: // [from doc] Submit new order failed.
case 134: // [from doc] Modify order failed.
case 135: // [from doc] Can't find order with ID =
case 136: // [from doc] This order cannot be cancelled.
case 137: // [from doc] VWAP orders can only be cancelled up to three minutes before the start time.
// 138, 129 vague
case 140: // [from doc] The size value should be an integer:
case 141: // [from doc] The price value should be a double:
case 142: // [from doc] Institutional customer account does not have account info
// 143 vague
case 144: // [from doc] Order size does not match total share allocation. To adjust the share allocation, right-click on the order and select “Modify > Share Allocation.”
// 145 vague
case 146: // [from doc] Invalid trigger method.
…
case 2102: // [from doc] Unable to modify this order as it is still being processed.
case 2109: // [from doc] Order Event Warning: Attribute “Outside Regular Trading Hours” is ignored based on the order type and destination. PlaceOrder is now processed.
// actual: Order Event Warning:Attempted modify of OutsideRth will not be done. Order modify now being processed.
// All the errors listed above are thought to be specific to Orders based on the message description.
// It is sometimes unclear from the message whether the order id will be available with the error, or not.
// Only errors with an id can be meaningfully routed to Order_error_Route.
// All the rest might as well be routed right away to ReqTracker_error_Route.
//
// In any case the above list of errorCode's are treated as a list of what should be preferentially routed to orders before request trackers.
// However the consequences of including an extraneous errorCode in the above list are non-existent for errors that turn out not to include a request id.
// For errors with a request id, an extraneous errorCode in the above list is consequential only if it is NOT an order error,
// but the request id coincidentally matches a currently-tracked order id.
// It is hard to comment meaningfully on how obscure that is.
// Instead it suggests motivation for a redesign that keeps order id's in their own numeric space,
// or interleaves them in the same numeric space with other request id's.
// … rest of long comment omitted
if (id != kIBNullReqId)
handled = Order_error_Route (id, errorCode, errorString);
else//10/14/12 was: if (!handled)
// So both kIBNullReqId errors and errors that are not successfully routed to an order based on the id will end up here.
handledBy = ReqTracker_error_Route (id, errorCode, errorString);
// see comment in default case re ReqTracker_error_Route details
break;
//////////////////////////////////////////////// //////////////////////////////////////////////// /////////////////
case 326: // error -1 326 Unable connect as the client id is already in use. Retry with a unique client id.
clientIdInUse = true;
break;
case 505: // actual: "request 2104 error 505 Fatal Error: Unknown message id."
// Do not route this message–there should be no use in doing so since the connection is about to be closed.
// Also see special case at top of this function.
break;
case 2100: // actual: New account data requested. API client has been unsubscribed from account data.
handledBy = accountUpdateReqTracker .error_Route (id, errorCode, errorString); // make sure AccountUpdateRequest::error_Generic gets this error
AccountUpdatesCancelledByServer (errorString);
break;
default:
handledBy = ReqTracker_error_Route (id, errorCode, errorString);
// calls ActiveRequestCategoryManager::error_Route which calls ActiveRequest::error_Route for each outstanding request
// which calls error_SpecificRequest for request with matching id if found
// and may call error_Generic which is currently only implemented by AccountUpdateRequest
// for error 2100 "New account data requested. API client has been unsubscribed from account data."
if (handledBy == NULL)
if (id != kIBNullReqId)
(void) Order_error_Route (id, errorCode, errorString);
break;
}
return handledBy;
}
それ以外の場合は、ReqTracker_error_Routeを呼び出します 。これも理想的ではありませんが、完全に適切であり、これを実行します。
ActiveRequest *MyWrapper::ReqTracker_error_Route (const int id, const int errorCode, const CString& errorString)
{
if (ActiveRequest *routedTo = historicalDataReqTracker .error_Route (id, errorCode, errorString))
return routedTo;
if (ActiveRequest *routedTo = marketDataReqTracker .error_Route (id, errorCode, errorString))
return routedTo;
if (ActiveRequest *routedTo = contractDetailsReqTracker .error_Route (id, errorCode, errorString))
return routedTo;
if (ActiveRequest *routedTo = realTimeBarReqTracker .error_Route (id, errorCode, errorString))
return routedTo;
if (ActiveRequest *routedTo = accountUpdateReqTracker .error_Route (id, errorCode, errorString))
return routedTo;
if (ActiveRequest *routedTo = marketDepthReqTracker .error_Route (id, errorCode, errorString))
return routedTo;
if (ActiveRequest *routedTo = fundamentalDataReqTracker .error_Route (id, errorCode, errorString))
return routedTo;
if (ActiveRequest *routedTo = scannerReqTracker .error_Route (id, errorCode, errorString))
return routedTo;
return NULL;
}
これは今度はこれを呼び出します
ActiveRequest *ActiveRequestCategoryManager::error_Route(const int id, const int errorCode, const CString& errorString)
{
// Route all errors to all objects, until/unless an object's error_Route returns non-NULL, indicating it has "handled" the error.
// In a typical implementation, generic errors (id == -1) will end up being routed to all objects,
// and specific errors will be ignored except by the object with a matching request id (if any),
// which will "handle" the error and return non-NULL (usually pointer to itself), preventing further looping here
// (and in current implementation preventing equivalent of further looping in MyWrapper::ReqTracker_error_Route which calls this function for each request category until it is "handled").
for (ActiveRequest *req = Next(); req != this; req = req->Next())
{
if (ActiveRequest *routedTo = req ->error_Route (id, errorCode, errorString))
return routedTo; // very likely routedTo will always equal req
}
return NULL;
}
そこで、 error_Routeの汎用実装が登場します。
ActiveRequest *ActiveRequest::error_Route(const int id, const int errorCode, const CString& errorString)
{
if (this->ReqId() == id)
{
error_SpecificRequest (id, errorCode, errorString);
return this;
}
if (id == kIBNullReqId)
{
if (errorCode == nonAPIErrorCode_connectionClosed)
Aborted (errorCode, errorString);
else
error_Generic (errorCode, errorString); // as of 5/3/12 this is the only call to error_Generic
}
return NULL;
}
また、 error_SpecificRequestの実装は、このような API リクエスト タイプごとにリクエスト追跡サブクラスによって提供されます。
void MarketDataRequest::error_SpecificRequest (const int id, const int errorCode, const CString& errorString)
{
if (errorCode == 200)
{
Aborted (errorCode, errorString);
}
/* market data error reference
error 39 200 No security definition has been found for the request
the same error can happen for reqMktData and reqContractDetails and reqHistoricalData
in this case it was a bogus combo (spread between AAPL and INTC options)
sending MarketDataRequest 39 AAPL,INTC SPREAD
but the "No security definition" error should be dispatched to the final request object
and should maybe delete without cancel
are there existing cases in which [??? text lost ???] caused an unintended cancel?
*/
}
したがって、アプローチの意味がわかるかもしれません。理想的ではありませんが、API を使用した作業のかなりの経験をサポートするアーキテクチャがたくさんあります。
-カート
[Q] 「重複したrequestID」の値についての考え方(APIから与えられた不正な値)
詳細:
私のプログラムでは、TWS への接続時に NextValidId を取得し、これを使用します。その日の最初の注文をする番号、そして注文するまでインクリメントし続けます。
昨日、ID によって返された NextValidId が重複していたことが起こりました。プログラムは数日前に注文 ID 638 の注文を出しており、ID 638 で別の注文を出した結果、両方の注文が実行されました。
TWSの「設定メニュー」>「注文」から、「拒否された注文を再利用する」オプションのチェックを外しました。
使用済みの OrderID が IB から返された理由を知っている人はいますか?
ありがとう、
ヴィシュルス
[A]フランク・ベル著
現在の注文番号の永続的なコピーを自分で保持する必要があることがわかりました。起動時に「max(自分の番号、NextValidId)」を使用します。
フランク
[A]リチャード・キング
TWS は、次の有効な ID を設定ファイルに保存します。これは大きな XML ファイルです。そして、それはTWSをシャットダウンしたときにのみ書き出されるのではないかと思います。これを確認することはできませんが、TWS がクラッシュした場合、または TWS をきちんと閉鎖せずに強制終了した場合、どのように処理されるかがわかるかもしれません。
リチャード
[A]ヤン・ボーネン著
念のため、EWrapper::openOrder( OrderId orderId, …) イベントの orderId も確認します。その注文 ID が次に使用されるものと等しいかそれより大きい場合、次に使用可能な値を orderId+1 に設定します。
「接続時に未決済注文をダウンロードする」オプションをチェックすると、開始時に未決済注文が自動的に受信されます。
asB
[Q] 「requestID空間分割」についての考え方
[A] by Kurt (C++ アーキテクチャの質問のスレッドから)
私は数週間前に非常によく似たものを開発していましたが、ロジックの一部はリクエスト ID の自動生成でした。その時点で、数か月前、もう簡単に見つけることができないトレッドの一部からリクエスト ID についてのあなたの意見を読んだことを思い出しました。私の記憶が正しければ、注文リクエスト ID を他のすべての種類のリクエストとは別に追跡しているとおっしゃっていました。
もしそうなら、私の質問は、「注文のリクエストID」スペースと「他のリクエストID」スペースが重複している場合、どのように区別するのでしょうか?例: 1 つの注文追跡セットと「その他のリクエスト」セットからの 1 つと一致する ID を持つ error() 呼び出しを取得しました。するとルートがあいまいになってしまいました。
私がこれまでこのアプローチでうまくいったからといって、それが最善であるとか、これからもこのアプローチでうまくいくというわけではないことに注意してください。私は差し迫った問題ではない問題に取り組むのが好きなので、さまざまな角度から検討し続けることができます。あるいは、無期限のままにしておくこともできます。 requestID スペースを分割している人もいます。リチャードはこれをやったと思います。十分に広い空間であると指摘されています。
私はたまたま小さな数字が好きなので、注文 ID には小さな数字を使用し、他のリクエスト ID には小さな数字を使用します。あなたの奇数/偶数のアイデア(以下)もこれに対処しますが、IBが次の整数値であなたの代わりに別のorderIDを自動的に挿入する可能性がある特定の種類の注文では問題が発生する可能性があるという記憶があります。
私がこれを回避する理由は、エラーコードスペースが IB によってある程度定義されているためです。IBドキュメントに基づくと、私が言及したが示さなかった MyWrapper::error_Route 関数には、特に errorCode に関する大きなスイッチと、注文に特有であることが知られているエラーの長いリストがあります。これは、注文関連のエラーの長いリストが切り捨てられた関数です。
ActiveRequest *MyWrapper::error_Route(const int id, const int errorCode, const CString& errorString)
{
if (errorCode == 505)
// actual: "request 2104 error 505 Fatal Error: Unknown message id."
printf ("generic error %d %s [id=%d]\n", errorCode, errorString.CStr(), id);
else if (id == kIBNullReqId)
printf ("generic error %d %s\n", errorCode, errorString.CStr());
else
printf ("request %d error %d %s\n", id, errorCode, errorString.CStr());
bool handled = false;
ActiveRequest *handledBy = NULL;
switch (errorCode)
{
// errors specific to Orders
case 103: // [from doc] Duplicate order ID.
case 104: // [from doc] Can't modify a filled order.
case 105: // [from doc] request 45 error 105 Order being modified does not match original order
case 106: // [from doc] Can't transmit order ID:
…
case 121: // [from doc] Invalid BD flag for the order. Check "Destination" and "BD" flag.
case 122: // [from doc] No request tag has been found for order:
// 123, 124 vague
case 125: // [from doc] Buy price must be the same as the best asking price.
case 126: // [from doc] Sell price must be the same as the best bidding price.
case 129: // [from doc] VWAP orders must be submitted at least three minutes before the start time.
case 131: // [from doc] The sweep-to-fill flag and display size are only valid for US stocks routed through SMART, and will be ignored.
case 132: // [from doc] This order cannot be transmitted without a clearing account.
case 133: // [from doc] Submit new order failed.
case 134: // [from doc] Modify order failed.
case 135: // [from doc] Can't find order with ID =
case 136: // [from doc] This order cannot be cancelled.
case 137: // [from doc] VWAP orders can only be cancelled up to three minutes before the start time.
// 138, 129 vague
case 140: // [from doc] The size value should be an integer:
case 141: // [from doc] The price value should be a double:
case 142: // [from doc] Institutional customer account does not have account info
// 143 vague
case 144: // [from doc] Order size does not match total share allocation. To adjust the share allocation, right-click on the order and select “Modify > Share Allocation.”
// 145 vague
case 146: // [from doc] Invalid trigger method.
…
case 2102: // [from doc] Unable to modify this order as it is still being processed.
case 2109: // [from doc] Order Event Warning: Attribute “Outside Regular Trading Hours” is ignored based on the order type and destination. PlaceOrder is now processed.
// actual: Order Event Warning:Attempted modify of OutsideRth will not be done. Order modify now being processed.
// All the errors listed above are thought to be specific to Orders based on the message description.
// It is sometimes unclear from the message whether the order id will be available with the error, or not.
// Only errors with an id can be meaningfully routed to Order_error_Route.
// All the rest might as well be routed right away to ReqTracker_error_Route.
//
// In any case the above list of errorCode's are treated as a list of what should be preferentially routed to orders before request trackers.
// However the consequences of including an extraneous errorCode in the above list are non-existent for errors that turn out not to include a request id.
// For errors with a request id, an extraneous errorCode in the above list is consequential only if it is NOT an order error,
// but the request id coincidentally matches a currently-tracked order id.
// It is hard to comment meaningfully on how obscure that is.
// Instead it suggests motivation for a redesign that keeps order id's in their own numeric space,
// or interleaves them in the same numeric space with other request id's.
// … rest of long comment omitted
if (id != kIBNullReqId)
handled = Order_error_Route (id, errorCode, errorString);
else//10/14/12 was: if (!handled)
// So both kIBNullReqId errors and errors that are not successfully routed to an order based on the id will end up here.
handledBy = ReqTracker_error_Route (id, errorCode, errorString);
// see comment in default case re ReqTracker_error_Route details
break;
//////////////////////////////////////////////// //////////////////////////////////////////////// /////////////////
case 326: // error -1 326 Unable connect as the client id is already in use. Retry with a unique client id.
clientIdInUse = true;
break;
case 505: // actual: "request 2104 error 505 Fatal Error: Unknown message id."
// Do not route this message–there should be no use in doing so since the connection is about to be closed.
// Also see special case at top of this function.
break;
case 2100: // actual: New account data requested. API client has been unsubscribed from account data.
handledBy = accountUpdateReqTracker .error_Route (id, errorCode, errorString); // make sure AccountUpdateRequest::error_Generic gets this error
AccountUpdatesCancelledByServer (errorString);
break;
default:
handledBy = ReqTracker_error_Route (id, errorCode, errorString);
// calls ActiveRequestCategoryManager::error_Route which calls ActiveRequest::error_Route for each outstanding request
// which calls error_SpecificRequest for request with matching id if found
// and may call error_Generic which is currently only implemented by AccountUpdateRequest
// for error 2100 "New account data requested. API client has been unsubscribed from account data."
if (handledBy == NULL)
if (id != kIBNullReqId)
(void) Order_error_Route (id, errorCode, errorString);
break;
}
return handledBy;
}
したがって、このコードは、エラー コードが注文を処理することが特にわかっている場合を除いて、最初にエラーを注文にルーティングしないという点で誤りです。これによる最悪の結果は、実際にはそれほど悪いことではありません。なぜなら、ほとんどの場合、オーダーコールバックメッセージが物事の基本的な状態を処理し、予期しない状態変化が発生した場合、最悪の場合、おそらく理由を説明するメッセージがログに記録されるからです。この時点で、コードを必要に応じて変更できます。
私は必ずしもこのアプローチを推奨しているわけではありませんが、私がとった方法です。
[A]リチャード・L・キング著
ID 空間の分割に関しては、次の範囲を使用します。
さまざまなリクエストタイプ。
市場データリクエスト: 0 – 0x3FFFFF
マーケットデプスリクエスト: 0x400000 – 0x7FFFFF
履歴データリクエスト: 0x800000 – 0x80FFFF
実行詳細リクエスト: 0x810000 – 0xFFFFFF
契約詳細リクエスト: 0x1000000 – 0xFFFFFFF
注文 ID: 0x10000000 – 0x7FFFFFFF
この方法でIDを分割すると、どのタイプの ID かを簡単に識別できるようになります。これらの大きさや配置には特に意味はありません。範囲は、すべての人にとって十分な大きさであると思います。
私の API 実装では、クライアントは範囲内の ID を使用してリクエストを作成します。0 から n まで。n はリクエストのタイプによって異なります。したがって、市場データのリクエスト ID は 0 ~ 4194303 の範囲になります。
実行詳細リクエスト 0 ~ 8323071。このアプローチにより、クライアントは
関連するハンドラーを見つけるための配列へのインデックスとしてのリクエスト IDリストを検索したりハッシュ検索を使用したりするのではなく、応答のオブジェクトを使用します。
リクエストが行われると、API コードは呼び出し元のリクエスト ID に対し適切な基本値を追加することで TWS の ID 空間を作成します。また、その逆の場合も同様で応答を返します。
注文 ID は、クライアントが関与しないという点で、それらを割り当てる際に若干異なる方法で処理されます。placeOrder メソッド自体が ID を指定された注文オブジェクトであり、すでにID が入っている(ゼロ以外の)場合、同じIDを使うと単なる注文変更として処理されます。明示的なmodifyOrderメソッドを使用することも可能です。
リチャード
[Q] トレーリングストップを変更しようとすると、エラー 10067 が発生しました
[A] by Josh
2017 年 12 月 13 日に追加
注文はすぐにはトリガーされず、19:25:02にトレイルストップ価格が割り当てられなかったようです。その場合には、トレイルストップ価格を指定せずに変更することができます。その後、市場で取引が発生し、システムがトレーリングストップ価格で注文を変更しました。
その段階で注文を再度変更しようとすると (19:25:06)、トレーリングストップ価格が変更されたため、システム内の注文と一致しなくなるためエラーが発生します。その時点で注文を変更するには、トレーリングストップ価格を示さない最初の orderStatus の代わりに、返された最新の orderStatus() メッセージ内の Order オブジェクトを使用する必要があります。
[Q] API接続確認・ティックの適時確認
[A1] by rwk2095
> 私は、ES や QQQQ などのリアルタイムデータをサブスクリプションしています。受け取るデータの入札やオファーを含むすべての更新にタイムスタンプを付けます。10 秒などの一定の時間間隔で更新時刻をチェックし、期限内にアップデートを受信しなかった場合は警報音を鳴らします。
そのアイデアは気に入っています、ありがとう。株式固有の問題は除外されますが、すべての接続性と市場固有の問題を個別の銘柄ごとにキャッチできるのは素晴らしいことです。
[A2]マイク・スミス 著
ポジションをエントリーする際、私の ATS は出来高の高速移動平均を監視し、出来高が特定のポイントを超えて加速または減速している場合 (バックテストで確認)、何か問題が起きたと判断して取引を行いません。何らかの理由でデータが来なくなった場合に対処する方法として有効です。
さて、あなたが説明している非常に遅い市場の場合は、バックテストする必要がありますが、同じことがわかるかもしれません。つまり、出来高がほとんどまたはまったくない場合は、データフィードの切断有無に関わらず理想的なエントリーポイントではないため、同じ手法が機能する可能性があります。
私は通知システム (テキスト メッセージと電子メール) を実装しており、何か異常なことが起こったときに作動します。場合によっては、その場にいて「人間的な」判断を下す必要がある場合があり、ティックの長い停止もその 1 つです。
マイク
[Q] 複数の約定済み注文ステータス更新の avgFillPrice を取得するにはどうすればよいですか?
[A] by alex_7880
orderStatus() は注文数の残り(未執行注文数)を返します。私のロジックは基本的に、注文が約定されたと判断する前に未執行注文数が 0 になるのを待つというもので、残り= 0 のときに返される「double avgFillPrice」を使用します。
[Q] 金融商品のティックサイズへの四捨五入について
[A] Mike Smith による
小数点以下の桁数の計算を自動化できます。これは私がとったアプローチであり、分数ティックを持つ ZB でも機能します※。私は、あなたが望むことを行うnearestTick()メソッドを含むSymbolInfoクラスを持っています。
class SymbolInfo {
// This is assigned a value in constructor (below)
private int tickSize;
////////////////////////////////////////////////////////////
// Note: These two class vars are used only by nearestTick()
//
// The value of nearestTickMultiplier is based on the number
// of digits in the fractional part of the tick size. It's
// 10 to the power of that many digits. So if the tick size
// is a penny (0.01) then the number of digits in the
// fractional part is 2 and nearestTickMultiplier is 100.
private long nearestTickMultiplier;
// The value of tickSizeMultiplied is the tick size multiplied
// by nearestTickMultiplier. It is used to calculate the price
// as a long int which is the smallest possible long int which
// can be used to calculate the price by converting it to a
// double and dividing by nearestTickMultiplier. This guarantees
// price is a power of 10 without roundoff errors.
private long tickSizeMultiplied;
// Another example would be for futures contracts which use 1/32
// as a tick size which is 0.03125. In this case the number of
// digits in the fractional part is 5 so nearestTickMultiplier
// would be 100000 and tickSizeMultiplied would be 3125.
//
// This would also come in handy for representing tick values of
// FOREX symbols where one currency relative to the Dollar might
// be something in the neighborhood of 0.000000000001 to 1 (or
// visa-versa the way things are going lately).
///////////////////////////////////////////////////////////////
// Constructor
public SymbolInfo(ContractDetails contractDetails) {
tickSize = contractDetails.m_minTick;
int n = getFractionDigitCount();
// nearestTickMultiplier is always 1 or greater.
nearestTickMultiplier = Math.round(Math.pow(10.0, n));
tickSizeMultiplied = Math.round(tickSize * nearestTickMultiplier);
}
/**
* Get number of digits in fractional part of tickSize
*
* @return Number of digits in fractional part of tickSize
*/
public final int getFractionDigitCount() {
double fraction = tickSize – ((long)tickSize);
String s = Double.toString(fraction);
int n = s.length();
if (s.contains(".")) {
if (s.equals("0.1")) {
n++; // force trailing zeros, just looks better (GC)
} else {
while (s.charAt(n-1) == '0') {
n–;
}
}
}
if (s.startsWith("0")) {
n -= 1; // remove leading zero
}
n–; // remove decimal point
return n;
}
/**
* Round given price to nearest tick boundary so
* the returned value is a multiple of the tick size
* for this instrument.
*
* @param price The price we want to adjust
* @return The price adjusted to the nearest tick boundary
*/
public final double nearestTick(double price) {
long ticks = Math.round(price/tickSize);
price = (double)(ticks * tickSizeMultiplied);
return price/nearestTickMultiplier;
}
}
[Q] MKT(成行)注文とLIMIT(指値)注文のどちらを使用すればよいですか?一部約定した場合をチェックするにはどうすればよいですか?
[A] tdrtw による
ロジャー、あなたは部分約定を持っていて、価格があなたにとってプラスの方向に動いたので、部分約定がペーパートレード上の利益になったと仮定します。
私が手動で取引していたときは、ストップリミット注文を使用してその価格を指定してエグジット価格でエントリーし、動きの速い市場で部分的に約定することがよくありました。時々、待っていると価格が戻ってきて、約定が完了することがありました。
完全自動システムに移行したとき、ルールを変更し、指値なしでストップ注文でエントリーすることにしました。これにより平均エントリースリッページは増加しましたが、私はそれから多くの利益を得ました。
1) これまでのところ、すべてのエントリーオーダーは常に約定しています。
2) エントリー注文が約定されたので、一瞬のうちにエグジット注文が出されます。これは、部分的な約定が公開されていて、これを管理するための複雑なソフトウェアルールが存在するよりもはるかに安全です。
3) 今では、動きの速い市場を捉え、かつてはストップリミットエントリーを使用できなかった注文で利益を得ることができます。
4) はい、時々スリッページが私に不利になることがあります。
全体として、システムのパフォーマンスに悪影響があることは見つかりませんでした。どちらかと言えば、完全に自動化されているためシステムのパフォーマンスが向上しており、チャートを見てその瞬間に追われることなく、自分の時間を有効活用してより良い作業を行うことができます。
私のシステムは、正確な価格での STOP-Limit エントリーよりも、STOP エントリーを使用したオートモードの方がはるかに安全です。
難しいかもしれないことは承知していますが、複雑なソフトウェアプログラムを作成して現在のルールを強制的に実行させるよりも、ソフトウェアに合わせてシステムを変更する方が良い場合もあります。
自動化されてからは、プログラムが簡単で、問題が発生する可能性が低い、はるかにシンプルなシステムを作成するようになりました。
それは単なる私の意見です。
幸運を!
ダミアン (別名 Exotric_Adriana_1982 😉)
[Q] なぜ PosixMQ が IPC にとって良いアイデアなのでしょうか? スレッドセーフなキュー (ミューテックス付き) を使用できないのはなぜですか?
[A]スタックオーバーフロー のNils Pipenbrinckによる
リアルタイム OS 環境では、固定スケジュールでのコードの実行を保証する必要があるという問題に直面することがよくあります。たとえば、正確に10 ミリ秒ごとに呼び出される関数があるとします 。それより早くも遅くもありません。
このような厳しいタイミング制約を保証するには、いかなる状況でもタイム クリティカルなコードをブロックしてはならないコードを作成する必要があります。posix スレッド同期プリミティブはここでは使用できません。
別のプロセス/スレッドによってすでにロックされている可能性があるため、タイム クリティカルなコードからミューテックスをロックしたり、セマフォを取得したりしないでください。
ただし、多くの場合、タイム クリティカルなコードから他のスレッドのブロックを解除することが許可されます (たとえば、セマフォの解放は問題ありません)。このような環境では、メッセージ キューは、ブロックすることなく、あるスレッドから別のスレッドにデータを渡すクリーンな方法を提供するため、データ交換に最適です。
単に変数を設定するためにキューを使用するのはやりすぎのように聞こえるかもしれませんが、これは非常に優れたソフトウェア設計です。この方法で実行すると、タイムクリティカルなコードへの明確に定義されたインターフェイスを実現できます。
また、競合状態の問題に遭遇することがないため、決定論的なコードを書くのにも役立ちます。メッセージキューを介して変数を設定すると、タイムクリティカルなコードが送信されたのと同じ順序でメッセージを確実に認識できるようになります。ダイレクト メモリ アクセスとメッセージを混在させる場合、これを保証することはできません。
[A2] by Alok スタックオーバーフロー から保存
メッセージキューは、2 つの異なるプロセス間でデータを交換する必要がある場合に、主にIPC メカニズムとして使用されます。ただし、メッセージキューはスレッドコンテキストの切り替えにも使用される場合があります。
たとえば: ドライバーの上にあるソフトウェア層にコールバックを登録します。コールバックはドライバーのコンテキストで返されます。これはドライバーによって生成されたスレッドです。
これで、ドライバのスレッド内で多くの処理を実行してこのスレッドを占有することがなくなりました。したがって、コールバックで返されたデータをメッセージキューに追加すると、データの処理を実行するためにアプリケーションスレッドがブロックされます。
[Q] 100% 完成したフレームワークからのフィードバック
[A] by poch32
セキュリティに応じて、私の ATS は現在 3 ~ 8 つの戦略をチェックしています。戦略には「特権リング」 (OS 用語で言えば) が与えられるため、衝突が発生した場合には、常に一方が他方の中で優先されます。
2 つの対立するアルゴリズムを使用できるシナリオがいくつかあります (エリックが以前に提案した、異なる時間枠のアルゴリズムが良い例です)
ただし、私の場合、すべての戦略が多かれ少なかれ同じ時間枠を指向しているため、2 つ以上の戦略が衝突することは許可できません。それは事実上、通信でお金をばら撒くことになるからです。
ATSの 2 つの独立した同時インスタンスが実行されており、同時戦略の衝突を許可する必要がある場合、遅かれ早かれ ATS の 1 つを別のマシンに移動する必要が生じる可能性があります。
[A] by tdrtw
私は複数の Future Contracts で複数のシステムを実行しています (ただし、同じ契約タイプで 2 つのシステムを実行することはありません)。
Windowsマシン上で実行され、Oracle XE データベースと通信するシェル Javaコンソールアプリを作成しました。
シェル Javaコンソールアプリは 1 つの金融商品 (以下、FI) のみを取引できます。これは、.jar ファイルを開始するときの入力パラメータであり、データベースコントロールテーブルにマップされ、有効期限、取引のリスクの大きさ、ティックサイズ、戦略タイプなどのすべての情報を取得します。
データベース管理テーブルは、どのような戦略で取引するかを指示します。Java コンソールアプリは、Oracle ストアドプロシージャへの汎用呼び出しを行い、Oracle ストアドプロシージャにコーディングされたばかりの正しい戦略を呼び出します。
基本的に、毎日起動するときに、Java コンソールアプリの複数のインスタンスを起動し、それぞれが独自の戦略を取引する異なる FI を指します。
何度も実装で間違えたので、うまくいくまでに時間がかかりました。しかし、一つ言えるのは、これは素晴らしく機能するということです。そして、ご存知のとおり、私はそれを少し誇りに思っています 🙂
私には Oracle データベースに関する以前のスキルがありましたが、何年も前に自動化事業を始めたときは Java のスキルはまったくありませんでした。そこで、Oracle Database のスキルに頼って、コードを Java と oracle pl/sql に分割しました。
今考えると大したことではありませんが、最初は少し大変でした。ただ立ち止まっているよりも、10歩進んで5歩下がるほうが良いでしょう。
また、これらのFAQに頼りすぎないように注意してください。かつて経験者の隣に座ったとき、自分が答えを導き出すことができる質問をし続けていたことを覚えています。その人が引っ越してからは、自分で物事を解決するようになり、沈んだり泳いだりしても実際にそれができることに自分自身で驚きました。
幸運を祈ります、あなたは最終的に自分の道を見つけるでしょう!
ダミアン
[A] リチャード・L・キング著
注: まず、IBController は現在、事実上 IBC に取って代わられることに注意してください。この変更の理由については、こちらをご覧ください。おそらく IBC への切り替えを検討する必要があります。
構成データに基づいて IBController の 1 つ以上のインスタンスを実行する完全に別個のデーモンプロセスを開発します。たとえば、月曜日の朝06:00 にライブ アカウントで IBController を実行し、金曜日の22:00 に (IBController のコマンド チャネル経由で) 停止します。これら 2 つの時点の間のいずれかの時点で IBControllerプロセスがドロップアウトした場合は、プロセスを再起動します。IBControllerService サンプルには、Windows 上でこれを実行するためのサンプル コードがすでに存在していることに注意してください (ただし、今説明したものよりも洗練されていません)。
実際、私は Windowsタスクスケジューラのみを使用して、上記と非常によく似たことをすでに実行しています。したがって、たとえば平日に停電が発生した場合、または IBController/TWS がクラッシュした場合 (実際にはクラッシュしませんが)、電力が再開されると IBController は自動的に再起動されますが、週末の故障の場合はそうではありません。
私が行っていないのは、TWS や API のアクティブな監視 (何らかの方法で監視すること) です。これは、11 年間、毎日取引日に一日中 API プログラムを実行してきた中で、その必要性を一度も見つけたことがないからです。(実際には問題が発生した可能性もあり、気付かなかっただけかもしれません。ただし、API を通じて 1 時間あたり数百件の注文をプッシュしたり、履歴データを継続的にダウンロードしたりしていれば、状況は異なるでしょう。)
また、ある意味で IBController が「準備完了」するまでトレーディング システムを待つ必要もないと思います。私のプラットフォームは、IBController または TWS が実行されているかどうかをまったく気にしません。そのように構成されている場合は、成功するまで定期的に接続を再試行し続けるだけです。実際、私はほとんどの API プログラムを、IBController/TWS/Gateway を実行しているマシンとは別のマシンで実行しています。
[Q]単体テスト – どれを選択しますか?
[A] by Dmitry (2015 年 5 月 7 日に追加)
C++ 単体テストに関する非常に優れた記事
「C++ 単体テスト フレームワーク ジャングルを探索する」
http://gamesfromwithin.com/exploring-the-c-unit-testing-framework-jungle
以下も参照してください:
http://stackoverflow.com/questions/1407354/is-there-ac-unit-testing-library-that-is-similar-to-nunit
単体テストの比較に関する優れた Wiki (他の言語もあります!)
http://en.wikipedia.org/wiki/List_of_unit_testing_frameworks
[その他の混沌とした実装メモ]
ティックモメンタムインジケーター
MarketMole によるアイデア
現在のビッド/アスクスプレッドに関する限り、取引がどちら側で約定されたかを実際に判断しようとするティックモメンタムインジケーターも構築しました。私がさまざまなブローカーに対してこれらを実行したことは知っているかと思いますが、私はいつかIBに対しても実行したいと思っており、広い意味で一般的な取引サイドを判断できたと思っていました。
トピック: 「何を[リモートで]監視するか」 Jason Sapp による
私は GUI を通じて実際のポジションを監視するのではなく、純損益、最悪の状況、未実現損益、実現損益、ポジション数 (ロング/ショート)、逃したポジションなどを監視するだけです。各ポジションを監視する機能を入れようと考えましたが、まだ実行していません。また、最終的には(必要に応じて)ポジションを手動でクローズする機能を実装したいと考えていますが、(もう一度言いますが)実際にそうする必要はありません。
どのようなものかを確認できるようにスクリーン ショットを添付しました (昨日はたまたま私にとって良い日だったので、リアルタイムグラフは非常にきれいに見えます)。添付のグラフの)黄色の線の終点付近で、最悪の場合の純損益が上回っている部分に異常値が出ていることはご容赦ください。これは、ポジションが決済される瞬間のデータを取得したものが紛れてしまったためで、バグを修正する時間が取れていないだけです。
直感的ではありませんが、IB はティックイベントを完了する際に価格とサイズの差分のみを送信します。
前の価格メッセージと同じ価格で取引が発生した場合、サイズメッセージのみを取得します(価格は暗黙的に以前の価格です)。
—————————————————————————————————
rwk: 注文ステータス イベントはログ記録にのみ使用します
zsolt: 最後の質問です。OrderStatus と Executions データを関連付けて、次のようにする価値があります。
同意しない場合は、TWS および/または ATS が機能することを確認するためにアラートが表示されます。
それとも、実行データは実際に信頼できるものですか?
—————————————————————————————————
私は(接続中に)Executionを見逃したことはありませんが、他の人はそういうケースがあるようです。したがって、IB は 100% 信頼できるわけではなく、execDetails を使用して再確認するコードを作成する必要があります。
接続が切断された場合にも必要になります。
—————————————————————————————————
よかった、ありがとう。先週、私は再確認コードを作成しました(名前も付けました)。私のクラスは OrderReconciler :) で、データベース内の永続化された状態を次のものと比較します。
注文と約定データをオープンし、約定とキャンセルをalg を使用してメモリとデータベースの状態を更新します。このようにして、コンテキストを失わずにアプリを再起動できますが、当然のことながら、取引の機会を逃す可能性があります。
これまでのところ、これは正しく動作しているようです。
execdetails について皆さんにご提案いただきありがとうございます。
—————————————————————————————————
そう、re updatePortfolio です。そしてあなたのシナリオは私と同じ理由です
rwk のアプローチは使用しないでください。ただし、対立するものを調整する場合は、お互い、特に取引中、updatePortfolio は潜在的にひどいものになる可能性がありますupdateportfolio で予測されない変更が発生した場合に便利です。
受け取った命令/実行メッセージによって、ユーザIDが自分のもので統一されているかを確認できます。注文をオープンしたり、執行を再度リクエストしたり、アラームを生成したりして確認できます。
その状態を検出するためのロジックは簡単ではなく、おそらく次のことが必要です。ある程度の時間差があれば、考えられるすべての注文の組み合わせを検討できる可能性があります。実行された、または部分的に実行された可能性があるなど。
-カート
—————————————————————————————————
[「ストップリミット注文の設計」に関する優れた実装メモ]
考えられる解決策:
OCO で逆指値注文と逆指値注文の両方を使用する
たとえば、10.00のストップを購入したいとします。
したがって、10.00 にストップリミット注文を出し、10.10 に買いストップ注文を出します。
確実に 1 つの注文だけがトリガーされるように OCO させます。
ContractTracker 実装に関する注意事項
「speculant」で ContracTracker クラスを作成しました。これは内部で 3 ~ 5 の IB API リクエストを処理し、フレームワークに履歴とリアルタイムデータ (価格ティック、リアルタイムバー) を提供します。
詳細は次のとおりです。
基本的に、IB は特定の契約の価格を取得する 5 つの方法を提供します。
reqMktData は、サンプリングされた価格更新をストリームします (たとえば、1 秒あたり 3 ~ 5 個の価格値を取得しますが、高値/安値を見逃す可能性があります)
reqHistoricalData (引数 "keep_up_to_date" を true に設定) – これにより、現在までの履歴から足が得られ、その後も足の更新情報が送信され続けます (これも 1 秒あたり 3 ~ 5 回、ただし 1 つの価格値だけではなく、全体の値) OHLC 値が更新された現在のバー) – 正確な制限は忘れましたが、50 を超える「リアルタイム」リクエストを同時に実行することはできないと思います。したがって、「3)」と「1)」も使用すると –これらすべてがカウントされるため、プラットフォーム内の各契約ごとに 3 種類すべての「リアルタイム」更新を行いたい場合、実際の制限は同時に約 50/3 = 16 契約となります。:(
reqRealTimeBars – これは、5 秒のバー更新を送信します (5 秒ごとに 1 回の更新)。
その他、数千の更新を伴う実際のティックへのサブスクリプションがありますが、Contractのサブスクリプションは 3 つまでに制限されています
さらに、すべてのビッドとアスクも取得できる「Depth」があります (レベル 2)
私の目標は、最大 50 件の契約をリアルタイムで追跡できるようにすることです。最初の 3 つだけを使用し、いくつかの履歴を要求するクラス (Java) を配置しますが、「最新の状態を保つ」を true に設定せずに (履歴のみを要求します)、次に 5 秒間のリアルタイム バーを要求します。サンプリングされた価格の更新用 (reqMktData)。
次に、それらを「マージ」し、受信したすべての更新を使用して独自の 1 分足、「改良された」5 秒足を作成します (5 秒ごとに 1 回更新する代わりに、ティック価格の更新ごとにバーが 1 秒あたり数回更新されました)。また、合成の 1 日バーも作成します。
この記事が気に入ったらサポートをしてみませんか?