AllTick
-
株式市場米国株香港株リアルタイム市場データ金融データ api株式市場データ

米国株APIはどう選べばデータ遅延を防げるのか?WebSocket・REST・プッシュ方式を比較

米国株APIの選び方を解説。RESTとWebSocketを比較し、ポーリングによる待ち時間、リアルタイム配信、再接続、クライアント処理が市場データの遅延に与える影響を紹介します。

AllTick読了 5 分

米国株のリアルタイム監視ダッシュボードを開発する際、開発者が特に頭を悩ませる問題の一つが、APIの接続自体は正常なのに、株価がすぐに更新されないことです。次のリクエストまで価格が表示されなかったり、一部の銘柄の価格が長時間変化しなかったり、サーバーではすでにデータを受信しているのに、フロントエンドに反映されなかったりすることがあります。

こうした問題は、必ずしもマーケットデータソース自体の遅延を意味するわけではありません。金融テクノロジーの開発者にとって、本当に確認すべきなのは、マーケットデータの発生からAPIによる転送、そしてクライアント側での表示に至るまでのデータ伝送経路全体です。

米国株APIを選ぶ際、RESTとWebSocketは代表的なデータ取得方式です。両者には、リクエストの仕組み、データ更新のタイミング、接続管理の面で違いがあります。これらの違いを理解することで、不要なポーリングリクエストを減らし、より安定した米国株リアルタイムプッシュシステムを構築できます。

米国株のマーケットデータ遅延は必ずしもAPIが原因ではない

マーケットデータがデータソースからユーザーインターフェースに届くまでには、通常、いくつかの段階を経ます。

  1. マーケットデータの取得: データソースで新しい約定情報や気配値が発生します。
  2. サーバー側の処理: マーケットデータが整理・処理されます。
  3. ネットワーク転送: データがサーバーから開発者のアプリケーションサーバーに送信されます。
  4. クライアント側の処理: アプリケーションがマーケットデータメッセージを解析し、キャッシュやビジネスロジック、フロントエンドを更新します。

いずれかの段階で待ち時間が発生すると、最終的な画面表示に遅延が生じる可能性があります。

例えば、ある監視システムが5秒ごとにREST APIを呼び出しているとします。APIが迅速にデータを返せる場合でも、2回のリクエストの間に株価が変動すると、アプリケーションは次のリクエストまで新しいデータを取得できません。

リクエスト間隔が一定で、価格変動のタイミングがランダムに近いと仮定した場合、ポーリングによる待ち時間は0~5秒程度となり、平均では約2.5秒です。ここにはHTTPのネットワーク往復時間やサーバー側の処理時間は含まれていません。

さらに重要なのは、RESTによる照会では通常、リクエスト時点の最新データのスナップショットが返されるということです。2回のリクエストの間に発生したすべての価格変動を取得できるわけではありません。

そのため、米国株のマーケットデータ遅延を分析する際には、次の3つの時刻を区別する必要があります。

  • tick_time:マーケットデータメッセージに含まれるイベント発生時刻。
  • received_at:アプリケーションサーバーがメッセージを実際に受信した時刻。
  • rendered_at:フロントエンドでデータの表示が完了した時刻。

この3つの時刻をそれぞれ記録することで、問題がデータ転送にあるのか、それともアプリケーション内部の処理にあるのかを判断しやすくなります。

なお、マーケットデータのイベント時刻とローカルでの受信時刻との差を計算する場合は、タイムスタンプの単位とタイムゾーンを事前に確認し、サーバーの時刻を同期しておく必要があります。サーバー内部の処理時間を測定する場合は、システム時刻の調整による影響を避けるため、単調増加時計(Monotonic Clock)を使用するとよいでしょう。

REST APIとWebSocketはどう選べばよいのか

RESTとWebSocketは互いに置き換えるものではなく、それぞれ異なるデータ取得ニーズに適しています。

比較項目REST APIWebSocket
データ取得方式クライアントからリクエストサーバーからプッシュ
接続方式独立したHTTPリクエスト継続的な接続
更新タイミングリクエスト後にデータを取得購読後に新しいメッセージを受信
主な用途最新気配値の照会、過去のKラインリアルタイムTick、板情報の更新
主な制約ポーリング間隔による待ち時間接続と購読状態の管理が必要

REST APIは、必要なときにマーケットデータを取得する用途に適しています。

例えば、ユーザーがAAPL.USの銘柄詳細ページを開いたとき、アプリケーションはREST APIを呼び出して最新気配値を取得したり、過去のKラインデータを取得してチャートを初期表示したりできます。

この方式では、長時間接続を維持する必要がありません。実装も比較的シンプルで、既存のバックエンドサービスにマーケットデータ照会機能を統合しやすいというメリットがあります。

一方、数十銘柄を継続的に監視するアプリケーションで高頻度のポーリングを行うと、リクエスト数が増加します。また、リクエスト頻度はAPIプランの呼び出し制限にも左右されます。ポーリング間隔を短くしても、2回のリクエストの間に発生したすべての価格変動を取得できるとは限りません。

WebSocketは、継続的なマーケットデータの更新に適しています。

WebSocketでは、接続を確立した後、クライアントが購読リクエストを送信します。サーバーは、購読条件に合致する新しいマーケットデータが発生すると、クライアントにプッシュできます。

例えば、米国株の監視ダッシュボードでAAPL.US、MSFT.US、NVDA.USを同時に購読する場合、銘柄ごとにHTTPリクエストを繰り返す必要はありません。継続的な接続を通じて、それぞれの銘柄のTickデータを受信できます。

これにより、繰り返し発生するリクエストの負荷を減らし、ポーリングによる待ち時間も省けます。

ただし、WebSocketを利用すれば遅延が完全になくなるわけではありません。上流のマーケットデータ更新、ネットワーク経路、メッセージキュー、フロントエンドのレンダリングなども、最終的な表示速度に影響します。WebSocketが解決するのはデータの配信方式の問題であり、データ伝送経路全体の遅延をなくすものではありません。

RESTで初期データを取得し WebSocketで継続的に更新する

過去のマーケットデータとリアルタイム価格の両方を表示するアプリケーションでは、必ずしも一方のAPIだけを選ぶ必要はありません。

より実用的なのは、RESTとWebSocketにそれぞれ異なる役割を持たせる方法です。

米国株リアルタイムマーケットデータの流れ

REST API
最新気配値と過去のKラインデータを取得
↓
アプリケーションサーバー
初期データの読み込み、購読管理、キャッシュ管理
↓
WebSocket
リアルタイムの約定情報とマーケットデータメッセージを継続的に受信
↓
フロントエンドのマーケット監視ダッシュボード
株価、約定情報、画面表示状態を更新
実際の接続では、REST APIを初回の銘柄情報や過去のマーケットデータの取得に使用し、WebSocketをその後のリアルタイム約定データの受信に使用できます。

例えば、AAPL.USの過去のKラインデータを先に取得し、その後、同じ銘柄のリアルタイムTickデータを購読します。新しい約定メッセージを受信したら、アプリケーションで最新価格を更新し、必要に応じてチャート表示を処理します。

ここで見落としやすいのが、WebSocketが切断された後のデータ欠損です。再接続に成功しても、切断中に発生したデータが自動的に補完されるわけではありません。

再接続後は、必要な銘柄をすべて含む購読リストを再送信し、RESTを使って最新スナップショットを取得し、現在の表示状態を調整する必要があります。切断中に発生したすべてのTickを完全に保存する必要がある場合は、対応する過去のTickデータを取得できる別のデータソースが必要です。最新気配値の照会を、完全なTick履歴の再取得として扱うことはできません。

AllTickを使用して米国株のリアルタイムマーケットデータを購読する

米国株の逐次約定データを取得したい開発者向けに、AllTickは株式専用のWebSocketデータインターフェースを提供しています。

米国株の銘柄コードは、市場サフィックスを付けた形式を使用します。例えば、AAPL.US、MSFT.US、NVDA.USなどです。株式と、外国為替や暗号資産などの他の金融商品では、異なるWebSocketゲートウェイを使用するため、接続先を混同しないようにする必要があります。

米国株の最新約定データを購読する場合、主に次のプロトコルを使用します。

操作cmd_id役割
約定データ購読リクエスト22004株式の購読リストを送信
約定データ購読応答22005購読リクエストを確認
約定データプッシュ22998リアルタイムTickを受信
ハートビートリクエスト22000接続を維持
ハートビート応答22001ハートビートを確認

これらのプロトコルは、AllTickの公式WebSocket接続ドキュメントで確認できます。

以下では、Pythonを使用して米国株のリアルタイム購読を実装する基本例を紹介します。この例では、購読確認、ハートビート、接続切断後の再接続を扱います。

依存パッケージをインストールします。

text
pip install websocket-client

API Tokenを設定して実行します。

text
import os
import json
import time
import threading
from datetime import datetime, timezone

import websocket

TOKEN = os.environ["ALLTICK_API_TOKEN"]

WS_URL = (
    "wss://quote.alltick.co/"
    "quote-stock-b-ws-api?token=" + TOKEN
)

SYMBOLS = [
    {"code": "AAPL.US"},
    {"code": "MSFT.US"},
    {"code": "NVDA.US"}
]


def run_connection():
    heartbeat_stop = threading.Event()
    heartbeat_started = threading.Event()

    def send_heartbeat(ws):
        while not heartbeat_stop.wait(10):
            if not ws.sock or not ws.sock.connected:
                return

            heartbeat = {
                "cmd_id": 22000,
                "seq_id": 2,
                "trace": "heartbeat",
                "data": {}
            }

            ws.send(json.dumps(heartbeat))

    def on_open(ws):
        subscribe = {
            "cmd_id": 22004,
            "seq_id": 1,
            "trace": "us_stock_sub",
            "data": {
                "symbol_list": SYMBOLS
            }
        }

        ws.send(json.dumps(subscribe))
        print("Subscription request sent")

    def on_message(ws, message):
        try:
            response = json.loads(message)
        except json.JSONDecodeError:
            print("Invalid JSON message")
            return

        cmd_id = response.get("cmd_id")

        # Check subscription response
        if cmd_id == 22005:
            if response.get("ret") == 200:
                print("Subscription confirmed")

                if not heartbeat_started.is_set():
                    heartbeat_started.set()

                    threading.Thread(
                        target=send_heartbeat,
                        args=(ws,),
                        daemon=True
                    ).start()
            else:
                print("Subscription failed:", response)

        # Receive real-time trade updates
        elif cmd_id == 22998:
            tick = response.get("data", {})

            received_at = datetime.now(
                timezone.utc
            ).isoformat(timespec="milliseconds")

            print({
                "symbol": tick.get("code"),
                "price": tick.get("price"),
                "tick_time": tick.get("tick_time"),
                "sequence": tick.get("seq"),
                "received_at": received_at
            })

    def on_error(ws, error):
        print("WebSocket error:", error)

    def on_close(ws, status_code, message):
        heartbeat_stop.set()
        print("Connection closed:", status_code)

    ws = websocket.WebSocketApp(
        WS_URL,
        on_open=on_open,
        on_message=on_message,
        on_error=on_error,
        on_close=on_close
    )

    ws.run_forever()
    heartbeat_stop.set()


# Reconnect and resubscribe after disconnection
while True:
    try:
        run_connection()
    except KeyboardInterrupt:
        print("Application stopped")
        break
    except Exception as error:
        print("Connection failed:", error)

    time.sleep(3)

このコードでは、主に3つの処理を行っています。

まず、株式専用のWebSocket接続を確立し、22004の約定データ購読リクエストを送信します。サーバーから22005が返され、正常な購読状態が確認された後に、アプリケーション側のハートビートスレッドを開始します。

次に、22998のメッセージを受信すると、銘柄コード、約定価格、マーケットデータのタイムスタンプ、シーケンス番号を取得し、ローカルの受信時刻も記録します。これらの情報は、その後の遅延監視に利用できます。

最後に、接続が切断された場合は再接続し、on_open内で購読リストを再送信します。

本番環境では、さらにバックオフ方式による再接続、ハートビート応答のタイムアウト検出、購読失敗時のアラート、メッセージキャッシュ、ログの永続化などを実装する必要があります。ここで使用している固定3秒の再接続間隔は、基本的な処理の流れを示すためのものであり、大規模な本番システムにそのまま適用できる完全な障害対策ではありません。

AllTickとの接続に関して、もう2つ注意すべき制限があります。

  • 同じWebSocket接続で購読リクエストを再送信すると、以前の購読リストが上書きされます。銘柄を追加するときは、引き続き購読したいすべての銘柄コードを含めて送信する必要があります。
  • WebSocketはKラインのプッシュ配信に対応していません。過去のKラインや最新のKラインが必要な場合は、WebSocketからの配信を待つのではなく、HTTP/RESTインターフェースを使用して取得する必要があります。

米国株のマーケットデータ遅延を判断するには どの指標を確認すべきか

APIの接続に成功したことは、データ接続の始まりにすぎません。長時間稼働するマーケット監視システムでは、データが継続的かつ適切なタイミングでクライアントに届いているかを確認することが重要です。

主に次の3つの項目を確認するとよいでしょう。

マーケットデータのタイムスタンプとメッセージ受信時刻

各メッセージのtick_timeと、アプリケーションサーバーでの受信時刻を記録します。マーケットデータの時刻が進んでいるにもかかわらず、受信時刻との間に大きな差が継続して発生する場合は、ネットワークやサーバー側の処理をさらに調査する必要があります。差を計算する前に、タイムスタンプの形式、タイムゾーン、サーバーの時刻同期を確認してください。

メッセージ処理と画面更新時刻

サーバーがメッセージをすでに迅速に受信しているのに、フロントエンドの株価が更新されない場合は、メッセージキューの滞留、クライアント側の状態更新、画面のレンダリングなどに問題がある可能性があります。メッセージの受信時刻とフロントエンドの表示完了時刻を個別に記録することで、アプリケーション内部の処理遅延をマーケットデータソースの遅延と誤認することを防げます。

購読状態とプランの制限

現在購読している銘柄コードがすべて含まれているか、WebSocket接続が正常に維持されているか、利用中のプランが必要な銘柄数と接続数に対応しているかを確認します。

例えば、AllTickのAll US Stocksプランでは、約11,000銘柄の米国株、最大10のWebSocket接続、1分あたり1,200回のAPI呼び出しが案内されています。大規模な米国株銘柄をカバーするアプリケーションでは、プランを選ぶ際に銘柄カバレッジ、接続数、リクエスト頻度を合わせて検討する必要があります。

AllTickの株式API製品ページでは、約150msの遅延参考値が表示されています。実際の環境に導入する際は、サーバーの設置地域、ネットワーク環境、クライアント側の処理状況に応じて、エンドツーエンドのテストを行う必要があります。この製品ページの数値を、すべてのユーザー環境における固定的な表示遅延として解釈してはいけません。

リアルタイムのマーケットデータ表示を主な目的とするシステムでは、RESTで初期データの取得や必要に応じた照会を行い、WebSocketで継続的に約定データを受信する構成が、比較的管理しやすい方法です。重要なのは、単純にリクエスト頻度を上げることではなく、アプリケーションの更新ニーズに合ったデータ取得方式を選ぶことです。

米国株のマーケットデータ監視システムを開発しているチームは、まずAllTickの株式API製品紹介を確認し、業務要件に応じてREST API公式ドキュメントとWebSocket接続チュートリアルを参照してください。実際に購読する銘柄を使用して、マーケットデータの更新状況と接続の安定性を検証できます。

今すぐマーケットデータ配信を開始

数秒で無料 API キーを発行し、1 つのエンドポイントからすべての市場に接続できます。