> For the complete documentation index, see [llms.txt](https://docs.infoway.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.infoway.io/sdk-kai-fa-gong-ju/infoway-node.js-sdk.md).

# Infoway Node.js SDK

Infoway 官方 Node.js / TypeScript SDK。覆盖 REST 行情、基础信息、市场概览、板块、个股、财务，以及行情 / 新闻 WebSocket。

[![npm version](https://img.shields.io/npm/v/infoway-sdk.svg)](https://www.npmjs.com/package/infoway-sdk) [![Node.js](https://img.shields.io/node/v/infoway-sdk.svg)](https://www.npmjs.com/package/infoway-sdk) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

| 项目           | 说明                                                                                                          |
| ------------ | ----------------------------------------------------------------------------------------------------------- |
| 包            | [`infoway-sdk@0.4.1`](https://www.npmjs.com/package/infoway-sdk)                                            |
| 运行环境         | Node.js 18+                                                                                                 |
| REST         | `https://data.infoway.io`                                                                                   |
| 行情 WebSocket | `wss://data.infoway.io/ws`                                                                                  |
| 新闻 WebSocket | `wss://data.infoway.io/news`                                                                                |
| 接口频率         | [HTTP](/getting-started/api-limitation/http.md) · [WebSocket](/getting-started/api-limitation/websocket.md) |
| 错误码          | [HTTP](/getting-started/error-codes/http.md) · [WebSocket](/getting-started/error-codes/websocket.md)       |
| 地址           | [行情地址](/getting-started/api-endpoints.md)                                                                   |

未传入 `apiKey` 时读取环境变量 `INFOWAY_API_KEY`。REST 方法均为 `async`。

### 目录

* 安装
* 快速开始
* 标的代码
* 客户端
* REST
* 类型化结果
* WebSocket
* 错误码
* 版本变更
* REST 路径

### 安装

```bash
npm install infoway-sdk
```

### 快速开始

```typescript
import { InfowayClient, KlineType } from "infoway-sdk";

const client = new InfowayClient();
console.log(await client.stock.getTrade("AAPL.US"));
console.log(await client.crypto.getKline("BTCUSDT", KlineType.DAY, 30));
console.log(await client.packages.getInfo());
```

### 标的代码

| 市场    | 格式          | 正确            | 错误          |
| ----- | ----------- | ------------- | ----------- |
| 美股    | `{代码}.US`   | `AAPL.US`     | `AAPL`      |
| 港股    | 5 位 + `.HK` | `00700.HK`    | `700.HK`    |
| A 股上海 | `{代码}.SH`   | `600519.SH`   | `600519.CN` |
| A 股深圳 | `{代码}.SZ`   | `000001.SZ`   | `000001.CN` |
| 日股    | `{代码}.JP`   | `7203.JP`     |             |
| 韩股    | `{代码}.KS`   | `005930.KS`   |             |
| 印股    | `{代码}.IN`   | `RELIANCE.IN` |             |
| 台股    | `{代码}.TW`   | `2330.TW`     |             |
| 加密货币  | 交易对         | `BTCUSDT`     |             |
| 外汇    | 货币对         | `USDJPY`      |             |

`basic` / `financial` 的 `type` 用品种类型（`STOCK_US`、`STOCK_CN`、`CRYPTO` 等），不要传市场码 `US`。

### 客户端

| 参数           | 默认                        | 说明       |
| ------------ | ------------------------- | -------- |
| `apiKey`     | `INFOWAY_API_KEY`         | API Key  |
| `baseUrl`    | `https://data.infoway.io` | REST 根地址 |
| `timeout`    | `15000`                   | 单次超时（毫秒） |
| `maxRetries` | `3`                       | 失败重试     |

| 入口                                                                     | 用途        |
| ---------------------------------------------------------------------- | --------- |
| `stock` / `crypto` / `japan` / `india` / `korea` / `taiwan` / `common` | 成交、盘口、K 线 |
| `basic` / `packages`                                                   | 标的、日历、套餐  |
| `market` / `plate`                                                     | 市场概览、板块   |
| `stockInfo` / `financial`                                              | 个股资料、财务   |

推荐枚举：`KlineType`、`SymbolType`、`Market`、`Lang`、`NewsLang`、`PeriodType`、`Business`、`RankSort`、`SortOrder`、`ScheduleType`。

### REST

多个标的用英文逗号分隔。频率见 [HTTP接口限制](/getting-started/api-limitation/http.md)。

#### 行情

| 方法                                            | 说明   |
| --------------------------------------------- | ---- |
| `getTrade(codes)`                             | 最新成交 |
| `getDepth(codes)`                             | 盘口   |
| `getKline(codes, klineType, count, options?)` | K 线  |

成交字段：`s` 代码、`p` 价格、`v` 量、`vw` 成交额、`t` 毫秒、`td` 方向。K 线在 `respList` 中，`t` 为秒。单标的最多 500 根；多标的时每个返回最近 2 根。

```typescript
await client.stock.getTrade("AAPL.US,TSLA.US");
await client.crypto.getDepth("BTCUSDT");
await client.korea.getTrade("005930.KS");
await client.taiwan.getTrade("2330.TW");
await client.crypto.getKline("BTCUSDT", KlineType.MIN_1, 100);
```

| 枚举                                            | 值    | 周期   |
| --------------------------------------------- | ---- | ---- |
| `MIN_1` / `MIN_5` / `MIN_15` / `MIN_30`       | 1–4  | 分钟   |
| `HOUR_1` / `HOUR_2` / `HOUR_4`                | 5–7  | 小时   |
| `DAY` / `WEEK` / `MONTH` / `QUARTER` / `YEAR` | 8–12 | 日及以上 |

#### 基础信息

日期 `YYYYMMDD`。`getTradingHours` 已弃用，请用 `getTradingSchedule`。

```typescript
import { Market, ScheduleType, SymbolType } from "infoway-sdk";

await client.basic.getSymbols(SymbolType.STOCK_US);
await client.basic.getSymbolInfo(SymbolType.STOCK_US, "AAPL.US");
await client.basic.getStockDetail(SymbolType.STOCK_US, "AAPL.US");
await client.basic.getAdjustmentFactors("AAPL.US", Market.US, "20260801", "20260815");
await client.basic.getTradingDays(Market.US, "20260801", "20260815");
await client.basic.getTradingSchedule();
await client.basic.getTradingScheduleByType(ScheduleType.ENERGY);
await client.basic.getMarkets();
await client.packages.getInfo();
```

#### 市场 / 板块 / 个股 / 财务

```typescript
import { Lang, Market, PeriodType, RankSort, SortOrder, SymbolType } from "infoway-sdk";

await client.market.getTemperature("HK,US", Lang.ZH_CN);
await client.market.getBreadth(Market.US, Lang.ZH_CN);
await client.market.getTurnover(Market.US);
await client.market.getIndexes(Lang.EN);
await client.market.getLeaders(Market.US, 10);
await client.market.getOverview(Market.US, Lang.ZH_CN);
await client.market.getRankCategories(Market.US);
await client.market.getRank(Market.US, "all", {
  sort: RankSort.CHG,
  order: SortOrder.DESC,
  limit: 30,
});

await client.plate.getIndustry("HK", 200);
await client.plate.getConcept("HK", 100);
await client.plate.getMembers("IN20293.HK");
await client.plate.getIntro("IN20293.HK");
await client.plate.getChart("HK", 50);

await client.stockInfo.getValuation("AAPL.US");
await client.stockInfo.getRatings("AAPL.US");
await client.stockInfo.getCompany("AAPL.US", "zh-CN");
await client.stockInfo.getPanorama("AAPL.US");
await client.stockInfo.getConcepts("AAPL.US");
await client.stockInfo.getEvents("AAPL.US", 20);
await client.stockInfo.getDrivers("AAPL.US");

await client.financial.getEarningStatus("AAPL.US", SymbolType.STOCK_US);
await client.financial.getIncomeStatement("AAPL.US", SymbolType.STOCK_US, PeriodType.FQ);
await client.financial.getRevenue("AAPL.US", SymbolType.STOCK_US);
await client.financial.getCashFlow("AAPL.US", SymbolType.STOCK_US, PeriodType.FY);
await client.financial.getBalanceSheet("AAPL.US", SymbolType.STOCK_US);
await client.financial.getStatistics("AAPL.US", SymbolType.STOCK_US);
await client.financial.getDividend("00700.HK", SymbolType.STOCK_HK);
await client.financial.getDividendPayout("AAPL.US", SymbolType.STOCK_US);
await client.financial.getEarnings("AAPL.US", SymbolType.STOCK_US, PeriodType.FQ);
```

排行 `key` 来自 `getRankCategories`。财务需要 `symbol` + `type`。`periodType`：`fq` 季报、`fy` 年报、`fh` 中报。

### 类型化结果

传入 `{ parse: true }`。

```typescript
const trades = await client.crypto.getTrade("BTCUSDT", { parse: true });
trades[0].price;
trades[0].time;
trades[0].turnover;

const candles = await client.crypto.getKline("BTCUSDT", KlineType.MIN_1, 100, { parse: true });
candles[0].close;
candles[0].change_percent;
```

| 原样                       | 归一化                 |
| ------------------------ | ------------------- |
| 价格 / 量为字符串               | `number`            |
| 成交 / 盘口 `t` 毫秒；K 线 `t` 秒 | `Date`              |
| REST `pc` / WS `pfr`     | `change_percent`    |
| K 线 `respList`           | 展平列表                |
| 盘口 `a`/`b` 列式            | `{ price, volume }` |
| `vw`                     | `turnover`          |

### WebSocket

#### 行情

`business` 必须与标的市场一致。`connect()` 会阻塞，请先挂住 Promise。单连接每分钟最多 60 帧，见 [WebSocket限制](/getting-started/api-limitation/websocket.md)。

```typescript
import { InfowayWebSocket, KlineType } from "infoway-sdk";

const ws = new InfowayWebSocket({ business: "crypto" });
ws.onTrade = (data) => console.log(data.s, data.p);
ws.onError = (err) => console.error(err);

const running = ws.connect();
ws.subscribeTrade("BTCUSDT,ETHUSDT");
ws.subscribeKline("BTCUSDT", KlineType.MIN_1);
ws.unsubscribeKline("BTCUSDT", KlineType.MIN_1);
ws.close();
await running;
```

股票成交类型：`subscribeTrade(codes, true)`。

| 行为                | 说明                                                |
| ----------------- | ------------------------------------------------- |
| 心跳                | 每 30 秒发送 `10010`，服务端不回包                           |
| 重连                | 断线后自动重连并补发当前订阅                                    |
| `onReconnect`     | 再次连上时触发                                           |
| `onDisconnect`    | 仅意外掉线。`close()` 不触发                               |
| HTTP 401          | 停止重连                                              |
| 订阅配额超限（516）       | 不断开连接，在同一连接上退避重订，首次延迟 `subscribeRetryMs`（默认 2000） |
| 静默重订              | 收到过推送后静默超过 `staleAfterMs`（默认 90000，`0` 关闭）自动重发订阅  |
| `Subscribe fail:` | 纯文本订阅失败以 `SUBSCRIBE_FAIL` 走 `onError`             |

| 方向 | 协议号                   | 说明              |
| -- | --------------------- | --------------- |
| 出  | 10000 / 10003 / 10006 | 订阅成交 / 盘口 / K 线 |
| 出  | 11000 / 11001 / 11002 | 退订              |
| 出  | 10010                 | 心跳              |
| 入  | 10002 / 10005 / 10008 | 推送              |
| 入  | 11010                 | 退订确认            |
| 入  | 200                   | 连接成功            |

ack 只表示请求被接受。多个代码必须合并成一个逗号串。

#### 新闻

地址 `wss://data.infoway.io/news`，需单独授权。每个 Key 仅允许一条新闻连接。

```typescript
import { InfowayNewsWebSocket, NewsLang } from "infoway-sdk";

const news = new InfowayNewsWebSocket({ lang: NewsLang.ZH_HANS });
news.onNews = (item) => console.log(item.title);
```

订阅 `10020`，退订 `11020`，推送 `10022`。再次订阅覆盖语言。

### 错误码

REST 看 `ret`，WebSocket 看 `code`。`508`–`514` 两套含义不同。完整列表见 [HTTP错误码](/getting-started/error-codes/http.md) 与 [WebSocket错误码](/getting-started/error-codes/websocket.md)。

```typescript
import { InfowayAPIError, InfowayAuthError, InfowayRateLimitError } from "infoway-sdk";

try {
  await client.stock.getTrade("INVALID");
} catch (e) {
  if (e instanceof InfowayAuthError) console.error(e.msg);
  else if (e instanceof InfowayRateLimitError) console.error(e.ret, e.msg);
  else if (e instanceof InfowayAPIError) console.error(e.message, e.traceId);
}
```

REST 限额约 1200 次/分钟/Key。无效 Key：REST 抛 `InfowayAuthError`；WebSocket 握手 HTTP 401 且不重连。

| REST `ret` | 说明        |
| ---------- | --------- |
| 200        | 成功        |
| 400        | 参数错误      |
| 500        | 服务端错误     |
| 501 / 502  | 频率超限      |
| 503        | K 线数量超限   |
| 505        | 标的数量超限    |
| 506 / 507  | 参数错误 / 缺失 |
| 508        | 标的不存在     |
| 509        | 权限过期      |
| 513        | 历史时间超出套餐  |
| 514        | 无权限       |

| WebSocket `code` | 说明                         |
| ---------------- | -------------------------- |
| 501 / 502        | 频率超限                       |
| 505              | 本连接订阅数超限（SDK 停止补发）         |
| 516              | 账号订阅配额超限（SDK 退避重订，不断连）     |
| 506 / 507        | 参数错误 / 缺失                  |
| 508–511          | API Key 过期 / 无效 / 为空 / 黑名单 |
| 512              | 连接数超限                      |
| 513              | 心跳超时                       |
| 515              | 非 JSON                     |
| 517–521          | 握手失败                       |

### 版本变更

**0.4.1** — WebSocket 订阅配额超限（516）不再终止连接：在同一连接上退避重订，重试与补发受每分钟 60 帧预算约束；服务端返回纯文本 `Subscribe fail:` 时以 `SUBSCRIBE_FAIL` 走 `onError`；收到 505 后停止看门狗补发，订阅组较多时自动放宽补发间隔；新增 `staleAfterMs`（默认 90000，`0` 关闭）与 `subscribeRetryMs`（默认 2000）；出站时间戳记录裁剪到 256 条；日股 / 印度示例改用 `.JP` / `.IN`（Yahoo 的 `.T` / `.NS` 返回 508）。

**0.4.0** — 请求上限本地校验（`MAX_SYMBOLS` 100、`MAX_KLINE_BARS` 500、多标的批量每标的 2 根）、财务 `type` 非法值本地拦截、`classify` 还原被 `ret=500` 包裹的业务错误码、`isTerminal` 判定不可重试错误后停止重连、API Key 解析口径统一（显式空串不再回落环境变量）、回调支持同步函数、客户端关闭后调用明确报错。

**0.3.0** — `packages`、`financial`、韩股 / 台股客户端，`RestErrorCode` / `WsErrorCode`、枚举参数（`Lang`、`Market`、`PeriodType`、`RankSort`、`ScheduleType`、`SortOrder`）、`InfowayIoError`。

**0.2.0** — `basic.*` 参数修正（破坏性）：

| 旧写法                               | 新写法                                                             |
| --------------------------------- | --------------------------------------------------------------- |
| `getSymbols("US")`                | `getSymbols(SymbolType.STOCK_US)`                               |
| `getSymbolInfo("AAPL.US")`        | `getSymbolInfo("STOCK_US", "AAPL.US")`                          |
| `getAdjustmentFactors("AAPL.US")` | `getAdjustmentFactors("AAPL.US", "US", "20260801", "20260814")` |
| `getTradingDays("US")`            | `getTradingDays("US", "20260801", "20260814")`                  |
| `getTradingHours()`               | `getTradingSchedule()`                                          |

WebSocket 回调改为 `data` 本体；`unsubscribeKline` 按周期退订；无效 Key 停止重连。

### REST 路径

`{market}` = `stock` / `crypto` / `japan` / `india` / `korea` / `taiwan` / `common`。完整列表见 [行情地址](/getting-started/api-endpoints.md)。

| 接口           | 路径                                  |
| ------------ | ----------------------------------- |
| 最新成交         | `GET /{market}/batch_trade/{codes}` |
| 盘口           | `GET /{market}/batch_depth/{codes}` |
| K 线          | `POST /{market}/v2/batch_kline`     |
| 品种 / 日历 / 财务 | `GET /common/basic/*`               |
| 市场 / 板块 / 个股 | `GET /common/v2/basic/*`            |
| 套餐           | `GET /package/info`                 |

### 许可证

MIT。API Key：[infoway.io](https://infoway.io)。
