
如何使用实时美股 API 构建实时股票自选列表?
本文介绍如何使用 WebSocket 构建实时美股监控列表,涵盖多股票订阅、心跳机制、断线重连、行情更新以及订阅容量规划。
股票自选列表在界面上看起来很简单:显示股票代码、最新价格,以及可能的涨跌幅。但当多只股票需要持续更新,而且不能依赖页面刷新时,真正的开发难点才会出现。
对于开发实时股票追踪工具或股票监控仪表盘的金融科技开发者来说,反复向 HTTP 接口发送请求来获取价格,并不总是最实用的方式。随着自选列表中的股票数量增加,频繁轮询不仅会产生不必要的请求,也会让行情更新的一致性更难保证。
更合适的方式是在服务端维持一个 WebSocket 连接,订阅用户选择的美股,并在收到新的成交数据时更新对应的自选股项目。下面将介绍如何使用 AllTick 实时美股 API 实现这一方案。
围绕持续的数据流设计自选股列表
一个实用的股票自选列表不能只有一个接收价格的连接,还需要建立一套机制,将每次收到的行情更新关联到正确的股票,并让仪表盘能够使用这些数据。
一个实际可用的架构主要包含三个部分:
- 行情数据连接: 后端服务连接美股 WebSocket 接口,并订阅指定股票。
- 行情处理: 后端接收成交数据,并维护每只股票的最新行情。
- 仪表盘界面: 前端从后端读取最新数据,并更新对应的股票行。
将行情数据连接放在后端,也有助于保护 API Token。如果直接将 Token 暴露在浏览器 JavaScript 中,访问者就可能查看并重复使用该凭证。
如果只监控少量股票,可以使用内存中的 Map 保存最新价格。当应用规模扩大,需要多个后端实例共享相同的行情数据时,则可以使用 Redis 或其他共享缓存。
通过 WebSocket 订阅多只美股
AllTick 为股票和指数提供专用的 WebSocket 接口。美股代码使用带有交易所标识的格式,例如 NVDA.US、MSFT.US 和 AMD.US。
订阅最新成交价使用协议 22004。订阅成功后,服务器会返回 22005 响应,随后通过 22998 协议推送成交数据。每条更新都包含股票代码,应用程序可以根据该代码判断需要更新自选列表中的哪只股票。
安装 Node.js WebSocket 依赖包:
npm install ws将 AllTick API Token 存储在服务端环境变量中,而不是直接写在应用程序代码里:
ALLTICK_API_TOKEN=your_api_token下面的代码示例实现了 WebSocket 连接、多个股票订阅、心跳发送以及接收价格数据后的保存。
const WebSocket = require("ws");
const { randomUUID } = require("crypto");
const token = process.env.ALLTICK_API_TOKEN;
if (!token) {
throw new Error("Missing AllTick API token");
}
const symbols = [
"NVDA.US",
"MSFT.US",
"AMD.US"
];
let ws;
let heartbeat;
let sequence = 0;
const latestQuotes = new Map();
function sendMessage(cmd_id, data = {}) {
if (ws?.readyState !== WebSocket.OPEN) return;
ws.send(JSON.stringify({
cmd_id,
seq_id: ++sequence,
trace: randomUUID(),
data
}));
}
function subscribeStocks() {
sendMessage(22004, {
symbol_list: symbols.map(code => ({ code }))
});
}
function connect() {
ws = new WebSocket(
`wss://quote.alltick.co/quote-stock-b-ws-api?token=${encodeURIComponent(token)}`
);
ws.on("open", () => {
console.log("Stock WebSocket connected");
subscribeStocks();
heartbeat = setInterval(() => {
sendMessage(22000);
}, 10_000);
});
ws.on("message", raw => {
let packet;
try {
packet = JSON.parse(raw.toString());
} catch (error) {
console.error("Invalid message:", error);
return;
}
if (packet.cmd_id === 22005 && packet.ret !== 200) {
console.error("Subscription failed:", packet.msg);
return;
}
if (packet.cmd_id !== 22998 || !packet.data) {
return;
}
const { code, price, volume, tick_time } = packet.data;
latestQuotes.set(code, {
price,
lastTradeSize: volume,
tickTime: Number(tick_time)
});
console.log("Updated watchlist:", code, latestQuotes.get(code));
});
ws.on("close", () => {
clearInterval(heartbeat);
console.log("Connection closed. Reconnecting...");
setTimeout(connect, 10_000);
});
ws.on("error", error => {
console.error("WebSocket error:", error);
});
}
connect();这个示例实现了行情数据接收层。应用程序可以通过自己的 WebSocket 或服务器发送事件(Server-Sent Events,SSE),将 latestQuotes 中的更新推送到前端。
需要注意的是,WebSocket 连接必须保持活跃。AllTick 要求每 10 秒发送一次心跳,如果超过 30 秒没有收到心跳请求,连接就会断开。上述示例也包含断线后的重新连接逻辑,并在重新连接后重新发送原来的订阅列表。
不重建整个自选列表也能更新对应股票
实时股票自选列表应该只更新与收到的行情消息对应的股票行。
例如,当收到 NVDA.US 的消息时,后端应该只更新 NVIDIA 的行情,而不应覆盖已经保存的 Microsoft 或 AMD 价格。
因此,code 字段非常重要。它可以作为行情更新与自选列表中对应股票之间的关联键。
还需要区分最新一笔成交的成交量和当天累计成交量。成交推送中的 volume 字段表示与最新一笔交易相关的成交量,不能直接将其作为全天累计成交量显示。
如果仪表盘需要展示价格涨跌幅,后端还需要获取前一交易日的收盘价,然后通过以下公式计算涨跌幅:

保存市场价格时,应保留原始小数值。在金融计算中,建议使用 Decimal 运算库,而不是完全依赖 JavaScript 普通浮点数运算。
另一个值得采用的保护措施是记录最后一次接收到行情更新的时间。这样,仪表盘就能区分刚刚更新的价格和连接中断后仍然显示的旧价格。
扩大自选列表前先规划订阅容量
监控 5 只股票的自选列表,与追踪数百只股票的监控仪表盘,对行情数据的需求并不相同。
AllTick 的可订阅产品数量取决于所使用的套餐。当前定价信息显示,Basic 套餐可选择 100 个产品,Premium 套餐为 200 个,Professional 套餐为 3,000 个。All US Stocks 套餐覆盖约 11,000 只美股,但单条连接同时订阅的产品数量上限为 3,000 个。
用户动态添加或删除股票时,还需要考虑一个重要的订阅机制:默认情况下,新的订阅请求会替换原来的订阅列表。因此,后端应该维护完整的自选股列表,并在列表内容发生变化时重新发送更新后的完整订阅列表。如果只发送新添加的股票,原有订阅可能会被替换。
对于只需要获取最新价格的应用程序,较轻量的报价获取方式可能就足够了。如果仪表盘需要持续接收推送的成交数据,则应该选择包含实时 Tick 行情权限的套餐。Free 套餐提供最新价格和收盘数据,而实时 Tick 行情则列在付费套餐的功能范围内。
AllTick 通过 REST 和 WebSocket 接口提供股票行情数据,因此开发者可以将基础行情信息与持续推送的行情数据流结合使用。在开发实时股票自选列表之前,可以查看 AllTick WebSocket API 文档和可用的 API 套餐,根据应用程序的实际需求选择合适的订阅容量。
一个可靠的实时股票追踪工具,并不只是一个能够不断变化价格的表格。它还依赖于正确的股票代码映射、持续的订阅机制、心跳处理以及合理的行情状态管理。只有将这些环节正确实现,才能为自选股规模不断扩大的监控仪表盘建立稳定的基础。