> For the complete documentation index, see [llms.txt](https://docscn.jkidata.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docscn.jkidata.com/rest-api-xie-yi-shuo-ming/k-xian-shu-ju-jie-kou-bars.md).

# 📊 K线数据接口（Bars）

获取指定交易产品的历史 K 线数据，包括开盘价、最高价、最低价、收盘价和成交量。

> Bars 接口每次只能查询一个产品，不支持通过逗号批量传递多个产品代码。

### 接口信息

| 项目   | 说明      |
| ---- | ------- |
| 请求方式 | `GET`   |
| 接口路径 | `/bars` |
| 返回格式 | CSV     |
| 字符编码 | UTF-8   |
| 身份认证 | API Key |
| 批量查询 | 不支持     |
| 分页查询 | 支持      |

### 请求参数

| 参数         | 类型      | 必填 | 说明                  |
| ---------- | ------- | -- | ------------------- |
| `exchange` | String  | 是  | 产品所属交易所，例如 `NASDAQ` |
| `symbol`   | String  | 是  | 产品代码，每次只能传入一个产品     |
| `interval` | String  | 是  | K 线周期，例如 `1day`     |
| `page`     | Integer | 否  | 分页页码，从 `1` 开始       |
| `key`      | String  | 是  | 用户的 API Key         |

### 基础请求示例

获取 AAPL 的日 K 线：

```
GET /bars?exchange=NASDAQ&symbol=AAPL&interval=1day&key=your_api_key
```

### 分页请求示例

获取第一页：

```
GET /bars?exchange=NASDAQ&symbol=AAPL&interval=1day&page=1&key=your_api_key
```

获取第二页：

```
GET /bars?exchange=NASDAQ&symbol=AAPL&interval=1day&page=2&key=your_api_key
```

### 支持的 K 线周期

| `interval` | 周期   |
| ---------- | ---- |
| `1min`     | 1分钟  |
| `5min`     | 5分钟  |
| `15min`    | 15分钟 |
| `30min`    | 30分钟 |
| `1h`       | 1小时  |
| `1day`     | 1日   |
| `1week`    | 1周   |
| `1month`   | 1月   |

### 单产品请求规则

`symbol` 每次只能传入一个产品代码。

正确示例：

```
symbol=AAPL
```

不支持：

```
symbol=AAPL,AMD
```

使用多个产品代码请求 Bars 时，接口可能只返回 CSV 表头而不返回任何 K 线记录：

```
utc,close,open,high,low,volume
```

客户端应在发送请求前检查 `symbol`，避免传入逗号分隔的多个代码。

### cURL 示例

#### 获取日 K 线

```
curl "${JKIDATA_BASE_URL}/bars?exchange=NASDAQ&symbol=AAPL&interval=1day&key=${JKIDATA_API_KEY}"
```

#### 获取指定分页

```
curl "${JKIDATA_BASE_URL}/bars?exchange=NASDAQ&symbol=AAPL&interval=1day&page=2&key=${JKIDATA_API_KEY}"
```

### Python 示例

```
import requests

params = {
    "exchange": "NASDAQ",
    "symbol": "AAPL",
    "interval": "1day",
    "page": 1,
    "key": "your_api_key",
}

response = requests.get(
    f"{BASE_URL}/bars",
    params=params,
    timeout=30,
)

response.raise_for_status()
print(response.text)
```

### Python CSV 解析示例

```
import csv
import io
import requests

params = {
    "exchange": "NASDAQ",
    "symbol": "AAPL",
    "interval": "1day",
    "page": 1,
    "key": "your_api_key",
}

response = requests.get(
    f"{BASE_URL}/bars",
    params=params,
    timeout=30,
)

response.raise_for_status()

bars = list(
    csv.DictReader(io.StringIO(response.text))
)

for bar in bars:
    print(
        bar["utc"],
        bar["open"],
        bar["high"],
        bar["low"],
        bar["close"],
        bar["volume"],
    )
```

将价格和成交量转换为数值：

```
parsed_bars = [
    {
        "utc": int(bar["utc"]),
        "open": float(bar["open"]),
        "high": float(bar["high"]),
        "low": float(bar["low"]),
        "close": float(bar["close"]),
        "volume": float(bar["volume"]),
    }
    for bar in bars
]
```

将 UTC 时间戳转换为时间：

```
from datetime import datetime, timezone

for bar in parsed_bars:
    bar["datetime"] = datetime.fromtimestamp(
        bar["utc"],
        tz=timezone.utc,
    )
```

### JavaScript 示例

```
const params = new URLSearchParams({
  exchange: "NASDAQ",
  symbol: "AAPL",
  interval: "1day",
  page: "1",
  key: "your_api_key",
});

const response = await fetch(
  `${BASE_URL}/bars?${params.toString()}`
);

if (!response.ok) {
  throw new Error(`HTTP ${response.status}`);
}

const csvText = await response.text();
console.log(csvText);
```

### JavaScript CSV 解析示例

Bars 响应字段均为固定的数值字段，可以按照 CSV 行读取：

```
const lines = csvText
  .trim()
  .split(/\r?\n/);

const header = lines[0].split(",");

const bars = lines
  .slice(1)
  .filter(Boolean)
  .map((line) => {
    const values = line.split(",");

    return Object.fromEntries(
      header.map((field, index) => [
        field,
        values[index],
      ])
    );
  });

console.log(bars);
```

转换为数值：

```
const parsedBars = bars.map((bar) => ({
  utc: Number(bar.utc),
  open: Number(bar.open),
  high: Number(bar.high),
  low: Number(bar.low),
  close: Number(bar.close),
  volume: Number(bar.volume),
}));
```

### 返回格式

接口返回 CSV 文本，第一行为字段名称：

```
utc,close,open,high,low,volume
1787875200,319.7,316.845,322.37,315.4504,22346643
1787788800,314.58,310.545,315.4,309.4001,32419233
1787702400,313.45,310.3,315.43,308.8001,34024486
1787616000,309.9,310.79,313.59,308.21,25869807
1787529600,310.34,311.47,313.36,309.97,34673582
```

> 以上价格和成交量为接口验证时的行情快照，仅用于说明返回结构。

### 返回字段

| 字段       | 类型      | 说明                       |
| -------- | ------- | ------------------------ |
| `utc`    | Integer | K 线对应的 UTC Unix 时间戳，单位为秒 |
| `close`  | Number  | 收盘价或当前周期最新价格             |
| `open`   | Number  | 开盘价                      |
| `high`   | Number  | 最高价                      |
| `low`    | Number  | 最低价                      |
| `volume` | Number  | 成交量                      |

### 数据排序

接口按照时间倒序返回数据：

```
最新数据 → 较早数据
```

因此：

* 第一条记录是当前页最新的 K 线。
* 后续记录的时间逐渐向前。
* 翻页时，页码越大，通常表示查询更早的历史数据。

如果业务需要按照时间正序处理，应在客户端排序。

Python：

```
bars.sort(
    key=lambda bar: int(bar["utc"])
)
```

JavaScript：

```
bars.sort(
  (a, b) => Number(a.utc) - Number(b.utc)
);
```

排序后顺序为：

```
最早数据 → 最新数据
```

### 当前周期数据

返回结果可能包含当前尚未结束的 K 线。

在当前周期结束前，以下字段可能继续变化：

* `close`
* `high`
* `low`
* `volume`

如果业务只需要已经结束的 K 线，应根据 `interval` 和 `utc` 判断第一条记录对应的周期是否已经结束。

### 分页说明

使用 `page` 参数可以继续获取更早的历史数据。

```
page=1    最近一页数据
page=2    更早一页数据
page=3    继续向前获取
```

建议：

1. 从 `page=1` 开始请求。
2. 保存当前页返回的最早时间戳。
3. 逐步增加 `page`。
4. 当响应只剩表头或没有数据行时停止翻页。
5. 根据 `utc` 对不同页面的数据进行排序和去重。

不要依赖固定的单页记录数量，不同周期返回的记录数量可能不同。

### 空数据判断

无可用数据或请求条件不匹配时，接口可能只返回 CSV 表头：

```
utc,close,open,high,low,volume
```

解析后数据行数量为 `0`，客户端应将其识别为空结果，而不是解析错误。

Python：

```
if not bars:
    print("没有更多 K 线数据")
```

JavaScript：

```
if (bars.length === 0) {
  console.log("没有更多 K 线数据");
}
```

### 数据缺失说明

某个 K 线周期内没有成交时，可能不会生成对应的 K 线记录。因此，相邻记录的时间戳不一定连续。

客户端不应自行将缺失周期理解为接口错误。如需连续时间序列，应根据业务需求补齐缺失周期。

### HTTP 响应说明

接口返回 CSV 数据，但响应头可能显示为：

```
text/plain; charset=UTF-8
```

客户端应按照 CSV 解析响应正文，不应仅依赖 `Content-Type` 判断格式。

### 错误处理

客户端不能只根据 HTTP 状态码判断请求是否成功，还应验证 CSV 内容和业务状态。

以下情况应视为失败或无有效数据：

* HTTP 状态码不是 `2xx`。
* API Key 无效或已经过期。
* 缺少 `exchange`、`symbol` 或 `interval`。
* `symbol` 包含多个产品代码。
* `interval` 不受支持。
* 响应正文为空。
* CSV 缺少预期表头。
* CSV 仅包含表头，没有数据行。

建议验证表头：

```
utc,close,open,high,low,volume
```

### 使用建议

* 每次请求只能传入一个 `symbol`。
* 使用准确的 `exchange` 和 `symbol` 组合。
* 首次加载时从 `page=1` 获取最近数据。
* 需要更多历史数据时逐页向前加载。
* 不要假设每一页返回固定数量的记录。
* 使用 `utc` 对分页结果进行排序和去重。
* 当前周期 K 线可能尚未结束，数据可能继续变化。
* 高频实时更新建议结合 WebSocket 行情。
* 正式 API Key 不应写入前端代码、公开仓库或公开日志。
