AllTick
-
香港株株式 apiマーケットデータフィンテック開発

港股APIでリアルタイム相場を取得する際、昼休みによるデータの空白時間をどう処理するか?

香港株APIでリアルタイム行情を取得する際、昼休み中にTick更新が停止することがあります。本記事では、取引時間、WebSocketの接続状態、Tick更新を分けて確認し、障害との誤判定を防ぐ方法を解説します。

AllTick読了 5 分

香港株式の相場監視システムを開発する際、昼休みはAPIの障害と誤認されやすい場面の一つです。

例えば、Tencent Holdings(700.HK)のリアルタイム相場を購読しているページで、午前11:59までは新しい約定データを受信できていたのに、12:00を過ぎると価格の更新が突然止まったとします。このとき、WebSocket接続は切断されておらず、ハートビートも正常ですが、サーバーから新しいTickがプッシュされなくなっています。

システムが相場の更新時刻だけで接続状態を判断していると、昼休み中に再接続を繰り返す可能性があります。さらに、午後の取引再開時に再購読やデータキャッシュ、フロントエンドの状態管理が適切でなければ、相場の更新再開が遅れることもあります。

この問題に対処するには、APIからデータがプッシュされ続けているかどうかだけを確認するのでは不十分です。取引時間、WebSocketの接続状態、個別銘柄のデータ更新状況を分けて判断する必要があります。

12:00以降、香港株式の相場は必ず止まるのか?

香港株式のリアルタイム相場を取得するプログラムを開発する前に、押さえておくべき点があります。すべての香港株式銘柄に、まったく同じ昼休みの取引ルールが適用されるわけではありません。

香港取引所の証券市場取引ルールによると、通常銘柄の継続取引時間は午前09:30~12:00と午後13:00~16:00です。つまり、通常銘柄は12:00~13:00の間、継続取引を行いません。

一方、取引所が指定する Extended Trading Securities は、12:00~13:00の延長午前取引セッションでも取引できます。また、半日取引日には別の取り扱いがあるため、通常の全日取引日の時間をそのまま適用することはできません。

開発時には、まず次のルールに基づいて取引時間を分類できます。

銘柄・取引日の種類午前12:00~13:00午後
通常銘柄・全日取引日09:30~12:00昼休み13:00~16:00
指定された延長午前取引対象銘柄・全日取引日09:30~12:00延長午前取引13:00~16:00
半日取引日09:30~12:00継続取引終了午後の取引なし

表中の時刻はすべて香港時間です。半日取引日のクロージングオークションなどの取引時間は、別途処理する必要があります。

したがって、複数の香港株式を同時に監視するシステムでは、毎日12:00になった時点で、すべての銘柄を一律に取引停止と判定してはいけません。取引カレンダーと各銘柄に適用される取引時間を確認して、昼休みに入る銘柄を判定してから、相場の異常検知を行う必要があります。

市場の状態とWebSocketの状態を分けて判定する

実際の開発で起こりやすい問題の一つは、「Tickを受信していない」ことを、そのまま「相場APIが機能していない」と判断してしまうことです。

この2つは同じ状態ではありません。

相場システムは、市場の取引状態、WebSocketの接続状態、銘柄ごとの相場更新状態という3つの独立した状態に分けて管理することを推奨します。

検知する状況具体的な状態適切な処理
通常の昼休み取引カレンダー上は昼休みで、ハートビートは正常、新しいTickはない最後に有効だった価格を保持し、再接続しない
取引時間中に一時的に約定がない接続は正常だが、特定銘柄の新しいTickがない元のタイムスタンプを保持し、約定の発生状況を確認する
購読失敗購読応答を受信したが、エラーコードが返されている銘柄コード、Token、アカウント権限を確認する
接続異常WebSocketが切断された、またはハートビートに長時間応答がない接続を再確立し、購読を復元する

特に、ハートビートと相場データのプッシュは区別する必要があります。

ハートビートは通信経路が正常かどうかを確認するためのものです。一方、Tickは実際の約定データの更新を表します。接続が正常であっても、すべての銘柄で継続的に約定が発生するとは限りません。

例えば、午前中の最後の約定が11:59:58に発生し、12:30になっても画面にその価格が表示されているのは正常です。この場合、元の約定時刻を保持し、状態を「昼休み」と表示するべきです。価格の更新時刻を12:30に書き換えてはいけません。

香港時間と銘柄ごとの取引カレンダーで休場状態を判定する

昼休みを接続異常と誤認しないようにするには、相場サービス側で取引時間を判定し、WebSocketデータを処理する前段に状態管理のレイヤーを設ける方法があります。

次のJavaScriptコードは、通常銘柄と指定された延長午前取引対象銘柄の継続取引状態を判定する例です。

text
function getHKMinutes(now = new Date()) {
  const parts = new Intl.DateTimeFormat("en-GB", {
    timeZone: "Asia/Hong_Kong",
    hour: "2-digit",
    minute: "2-digit",
    hourCycle: "h23"
  }).formatToParts(now);

  const hour = Number(
    parts.find(p => p.type === "hour").value
  );
  const minute = Number(
    parts.find(p => p.type === "minute").value
  );

  return hour * 60 + minute;
}

function getHKSession({
  now = new Date(),
  isTradingDay,
  isHalfDay = false,
  isExtendedTradingSecurity = false
}) {
  if (!isTradingDay) return "MARKET_CLOSED";

  const time = getHKMinutes(now);

  if (time >= 570 && time < 720) {
    return "MORNING_SESSION";
  }

  if (isHalfDay && time >= 720) {
    return "HALF_DAY_AFTER_CONTINUOUS_SESSION";
  }

  if (
    time >= 720 &&
    time < 780 &&
    isExtendedTradingSecurity
  ) {
    return "EXTENDED_MORNING_SESSION";
  }

  if (time >= 720 && time < 780) {
    return "LUNCH_BREAK";
  }

  if (time >= 780 && time < 960) {
    return "AFTERNOON_SESSION";
  }

  return "OUTSIDE_CONTINUOUS_SESSION";
}

ここでは、2つの点に注意が必要です。

1つ目は、isTradingDay、isHalfDay、isExtendedTradingSecurity の値を、取引カレンダーと銘柄ごとの適用ルールから取得することです。月曜日から金曜日までという曜日だけで判断してはいけません。香港株式市場の祝日、半日取引日、特別な取引時間の変更は、実際の取引時間に影響します。

2つ目は、このコードが判定するのは継続取引時間であり、プレオープニングセッションやクロージングオークションは含まれないことです。本番環境では、完全な取引カレンダーに基づいて、それらの状態も追加する必要があります。このコードを香港取引所の完全な取引カレンダーとして扱うことはできません。

AllTickのWebSocketは昼休みにどう処理すればよいか?

継続的な相場更新が必要なアプリケーションでは、HTTP REST APIを繰り返しポーリングするよりも、WebSocketの方が逐次的な約定データの受信に適しています。

AllTickで香港株式の相場を取得する場合、株式には専用のWebSocket接続先を使用します。700.HKを例にすると、最新約定の購読には22004を使用し、成功応答は22005、その後の約定データは22998でプッシュされます。

接続が確立したら、次の購読メッセージを送信します。

text
{
  "cmd_id": 22004,
  "seq_id": 1,
  "trace": "hk-trade-sub",
  "data": {
    "symbol_list": [
      { "code": "700.HK" },
      { "code": "1288.HK" }
    ]
  }
}

昼休み中は、新しい約定を受信できないことを理由にWebSocket接続を自動的に閉じるのではなく、接続を維持するのが適切です。

AllTickのアプリケーション層のハートビートプロトコルでは、22000を送信し、サーバーから22001の応答を受け取ります。公式の接続説明では、10秒ごとにハートビートを送信する必要があり、30秒を超えてハートビート要求がない場合、接続は切断されます。

Node.jsでは、wsライブラリを使って相場データの受信、ハートビート検知、切断後の再接続を実装できます。

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

text
npm install ws

サーバー側でALLTICK_API_TOKEN環境変数を設定し、次のコードを使用します。

text
const WebSocket = require("ws");

const token = process.env.ALLTICK_API_TOKEN;

if (!token) {
  throw new Error("Missing AllTick API Token");
}

const symbols = ["700.HK", "1288.HK"];

let ws;
let heartbeatTimer;
let reconnectTimer;
let retryMs = 1000;
let sequence = 0;
let lastHeartbeatAck = 0;

const HEARTBEAT_INTERVAL_MS = 10000;

// クライアント側で設定するハートビート応答の監視しきい値
const HEARTBEAT_ACK_TIMEOUT_MS = 30000;

function send(cmdId, data = {}) {
  if (!ws || ws.readyState !== WebSocket.OPEN) {
    return;
  }

  const seqId = ++sequence;

  ws.send(JSON.stringify({
    cmd_id: cmdId,
    seq_id: seqId,
    trace: `hk-${cmdId}-${seqId}`,
    data
  }));
}

function connect() {
  ws = new WebSocket(
    `wss://quote.alltick.co/quote-stock-b-ws-api?token=${encodeURIComponent(token)}`
  );

  ws.on("open", () => {
    console.log("WebSocket connected");

    retryMs = 1000;
    lastHeartbeatAck = Date.now();

    // 再接続のたびに購読リスト全体を送信する
    send(22004, {
      symbol_list: symbols.map(code => ({ code }))
    });

    clearInterval(heartbeatTimer);

    heartbeatTimer = setInterval(() => {
      if (
        Date.now() - lastHeartbeatAck >
        HEARTBEAT_ACK_TIMEOUT_MS
      ) {
        console.error("Heartbeat acknowledgement timeout");
        ws.terminate();
        return;
      }

      send(22000);
    }, HEARTBEAT_INTERVAL_MS);
  });

  ws.on("message", raw => {
    let message;

    try {
      message = JSON.parse(raw.toString());
    } catch (error) {
      console.error("Invalid server message:", error.message);
      return;
    }

    // 購読結果
    if (message.cmd_id === 22005) {
      if (message.ret !== 200) {
        console.error(
          "Subscription failed:",
          message.ret,
          message.msg
        );
      } else {
        console.log("Subscription accepted");
      }
    }

    // ハートビート応答
    else if (message.cmd_id === 22001) {
      lastHeartbeatAck = Date.now();
    }

    // 最新約定のプッシュ
    else if (message.cmd_id === 22998) {
      onTick(message.data);
    }
  });

  ws.on("close", () => {
    scheduleReconnect();
  });

  ws.on("error", error => {
    console.error("WebSocket error:", error.message);
    ws.terminate();
  });
}

function scheduleReconnect() {
  clearInterval(heartbeatTimer);

  if (reconnectTimer) return;

  const delay = retryMs + Math.floor(Math.random() * 500);

  reconnectTimer = setTimeout(() => {
    reconnectTimer = null;
    connect();
  }, delay);

  retryMs = Math.min(retryMs * 2, 30000);
}

function onTick(tick) {
  // 取引所イベントの時刻を保持し、ローカルでの受信時刻を別に記録する
  console.log({
    ...tick,
    received_at: Date.now()
  });
}

connect();

このサンプルには、相場データの購読、ハートビート要求、ハートビート応答の処理、約定データの受信、切断後の指数バックオフによる再接続が含まれています。サンプル内の30秒というハートビート応答タイムアウトは、クライアント側で独自に設定した監視しきい値です。AllTickが応答遅延について保証するSLAではありません。

実際に運用する際には、さらに2つの点に注意が必要です。

1つ目は、22005の応答結果を確認することです。購読失敗の応答を受け取った場合は、銘柄コード、Token、アカウント権限を先に確認してください。原因を確認せずに再接続を繰り返すべきではありません。

2つ目は、購読リストの上書きルールです。AllTickでは、新しい購読リクエストを送信すると、以前の購読リストが置き換えられます。そのため、再接続後はsymbolsに含まれる銘柄をすべて再送信する必要があります。そうしなければ、以前購読していた銘柄の一部で相場更新が再開しない可能性があります。

午後の取引再開後、相場が正常に更新されたことをどう確認するか?

13:00になったからといって、すべての銘柄で直ちに新しい約定が発生するわけではありません。

システムは、取引カレンダーに基づいて通常銘柄の状態をLUNCH_BREAKからAFTERNOON_SESSIONに切り替える必要があります。ただし、実際に相場が更新されたかどうかは、受信したデータに基づいて判断します。

次の順序で確認することを推奨します。

  1. WebSocketが接続されたままか、または切断後に再接続できているかを確認する。
  2. 購読応答22005が成功を返していることを確認する。
  3. 22998を受信したら、該当銘柄の価格、約定時刻、キャッシュを更新する。
  4. ハートビートは正常でもTickが届かない場合は、その銘柄で新しい約定が発生しているかを確認し、REST相場照会でクロスチェックする。

ここでもう一つ見落としやすい点があります。相場データの時刻と、システムがデータを受信した時刻を混同してはいけません。

例えば、午後13:02に受信したTickの約定時刻が13:01:58である可能性があります。システムでは、APIが提供する元のtick_timeを保持し、ローカルでメッセージを受信した時刻も別に記録する必要があります。この2つの時刻の差から、データ転送や処理の過程に遅延が発生しているかどうかを判断できます。

分足K線を表示するアプリケーションでも、WebSocketでTickを受信できなかったことを理由に、約定データを自動的に補完してはいけません。AllTickのWebSocketはK線プッシュに対応していないため、K線データはHTTP REST APIから取得する必要があります。Tickを使って独自に分足K線を集計する場合は、実際の取引時間と約定データに基づいて処理してください。実際の約定がない時間帯に、新しい取引が発生したかのようなデータを生成してはいけません。

香港株式の相場システムで昼休みを正しく処理する

香港株式APIの接続時に発生する昼休みのデータ空白は、取引カレンダーの管理とリアルタイム相場接続の管理を連携させる必要がある問題です。

取引カレンダーは、現在の銘柄が取引時間内にあるかどうかを判定します。WebSocketのハートビートは通信状態を確認し、Tickの時刻は実際の約定データの更新を示します。この3つを分けて管理することで、通常の昼休みをAPI障害と誤認することを防ぎ、午後の取引再開後に本当のデータ異常をすばやく検知できます。

AllTickは、香港株式のリアルタイム約定データと板情報を取得するためのAPIを提供しています。開発者は株式WebSocketを通じて最新のTickを受信し、REST APIと組み合わせて最新相場や過去のK線を取得できます。複数の香港株式を同時に監視する相場サービスでは、銘柄の購読リスト、取引時間、相場キャッシュをサーバー側で一元管理することも可能です。

香港株式のリアルタイム相場監視システムを開発している場合は、AllTickの公式製品ページで株式相場APIの接続方法を確認し、実際の銘柄コードを使って購読権限、取引時間、データ更新の流れを検証してください。

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

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