台积电、鸿海、联发科、台达电,全球半导体和电子产业链上的这些关键公司,都在台湾股市挂牌。想开发台股行情应用、量化策略或者监控工具,第一步就是找到稳定的实时数据来源。
现在,Infoway API 正式上线台湾股票数据,覆盖台湾证券交易所(TWSE)上市股票和证券柜台买卖中心(TPEX)上柜股票,共约 2350 只。本文会介绍台股的基本规则,再用 Python 示例一步步演示如何查询实时成交、K线和盘口,以及如何通过 WebSocket 订阅行情。
1. 接入前要了解的台股规则
台股的交易规则和 A 股、港股都有差异,有几点会直接影响数据处理:
| 项目 | 规则 |
|---|---|
| 交易市场 | 台湾证券交易所(TWSE,上市)、证券柜台买卖中心(TPEX,上柜) |
| 常规交易时间 | 周一至周五 09:00–13:30(UTC+8),中午不休市 |
| 集合竞价 | 开盘 08:30–09:00,收盘 13:25–13:30 |
| 涨跌幅限制 | ±10% |
| 交易单位 | 1 张 = 1000 股,不足 1000 股属于零股 |
| 计价货币 | 新台币(TWD) |
以上是证交所公布的交易制度。其中最容易踩坑的是交易单位:台股习惯用“张”来说成交量,而 Infoway API 返回的成交量按股计算,展示时要注意换算。
2. 获取台股实时行情的两种方式
2.1 向交易所申请授权
台湾证交所官网的基本行情可以免费浏览,但如果要把实时数据接入自己的产品,就需要申请授权。根据证交所最新的收费标准,非券商和期货商的申请者,主要费用如下:
| 费用项目 | 金额(新台币) |
|---|---|
| 传输授权费 | 60,000 元/月 |
| 信息费(公网传输、报价自动更新,0–5000 户) | 100,000 元/月 |
| 信息费(超过 5000 户) | 每增加 1 户,12 元/月 |
| 非展示用途授权费(交易、风控、投资组合管理等) | 另行公告 |
以一款自动刷新报价的 App 为例,仅这两项的起步费用就是每月 16 万新台币。而且这只是证交所一家的费用,不包括柜买中心的上柜股票行情、系统开发和专线等成本,还需要签约。这条路更适合有一定规模的机构,个人开发者和小团队很难承担。
2.2 通过第三方数据服务商接入
证交所官方也提供了另一条路径,即通过签约信息公司获取实时数据。这种方式不需要自己对接交易所,上线也更快。
Infoway API 属于第三方数据服务商。一个 API Key 就能获取上市和上柜股票的行情,接口格式也与美股、港股、A 股等市场保持一致。付费套餐 99 USDT/月起,并提供 7 天免费试用,无需绑定信用卡。
3. Infoway API 提供哪些台股数据
| 数据类型 | 内容 | HTTP | WebSocket |
|---|---|---|---|
| 实时成交明细 | 价格、成交量、成交额、交易方向 | 支持 | 支持 |
| K线 | 1 分钟至年K,含开高低收、成交量、成交额、涨跌幅、涨跌额 | 支持 | 支持 |
| 一档盘口 | 买一、卖一的价格和数量 | 支持 | 支持 |
| 基础信息 | 公司名称、交易所、币种、每股盈利、股息率、所属板块等 | 支持 | 不支持 |
| 交易日与交易时间 | 台股交易日历、交易时段 | 支持 | 不支持 |
| 财务报表 | 季度和年度三大报表、收入明细、派息历史等 | 支持 | 不支持 |
所有台股代码的格式都是代码.TW,上市和上柜股票使用相同的后缀:
| 公司 | 代码 | 市场 |
|---|---|---|
| 台积电 | 2330.TW | TWSE |
| 鸿海 | 2317.TW | TWSE |
| 联发科 | 2454.TW | TWSE |
| 台达电 | 2308.TW | TWSE |
| 环球晶 | 6488.TW | TPEX |
如果需要区分上市和上柜,可以查询基础信息接口的exchange字段,见 4.6 节。
4. 台湾股票行情API使用教程
先在 Infoway 官网注册账号并获取 API Key,所有 HTTP 请求都要在请求头中带上它。下面的示例基于 Python,需要先安装依赖:
pip install requests websockets示例中的2330.TW(台积电)是免费试用可以查询的台股标的,注册后可以直接运行。
4.1 查询台股代码清单
可以在账户后台下载清单文件,也可以通过接口查询,产品类型传STOCK_TW:
import requests
url = "https://data.infoway.io/common/basic/symbols"
headers = {"apiKey": "YOUR_API_KEY"}
response = requests.get(url, headers=headers, params={"type": "STOCK_TW"})
symbols = response.json()["data"]
print(f"共 {len(symbols)} 只")
print(symbols[:3])清单会随新股上市和退市定期更新,建议定期同步。
4.2 查询实时成交明细
请求地址:GET https://data.infoway.io/taiwan/batch_trade/{codes}
codes是股票代码,多只股票用英文逗号分隔,单次最多 100 只。
import requests
url = "https://data.infoway.io/taiwan/batch_trade/2330.TW"
headers = {"apiKey": "YOUR_API_KEY"}
response = requests.get(url, headers=headers)
print(response.json())返回示例:
{
"ret": 200,
"msg": "success",
"traceId": "5817b491-36aa-4fd6-8725-d6371206e4da",
"data": [
{
"s": "2330.TW",
"t": 1793847506767,
"p": "2265.00",
"v": "1000.00",
"vw": "2265000.00",
"td": 0
}
]
}| 字段 | 说明 |
|---|---|
s | 股票代码 |
t | 成交时间,毫秒时间戳 |
p | 成交价 |
v | 成交量(股) |
vw | 成交额,注意不是均价 |
td | 交易方向,0:默认值,1:买入,2:卖出 |
4.3 查询K线
请求地址:POST https://data.infoway.io/taiwan/v2/batch_kline
台股K线接口只支持 POST,参数放在 JSON 请求体中:
| 参数 | 必填 | 说明 |
|---|---|---|
klineType | 是 | K线周期,见下表 |
klineNum | 是 | K线数量,单只股票最多 500 根;同时查询多只时,每只最多 2 根 |
codes | 是 | 股票代码字符串,多只用英文逗号分隔,最多 100 只 |
timestamp | 否 | 结束时间(秒),不传则返回最新数据 |
| klineType | 周期 | klineType | 周期 |
|---|---|---|---|
| 1 | 1 分钟 | 7 | 4 小时 |
| 2 | 5 分钟 | 8 | 日K |
| 3 | 15 分钟 | 9 | 周K |
| 4 | 30 分钟 | 10 | 月K |
| 5 | 1 小时 | 11 | 季K |
| 6 | 2 小时 | 12 | 年K |
示例:查询台积电最近 10 根日K。
import requests
url = "https://data.infoway.io/taiwan/v2/batch_kline"
headers = {"apiKey": "YOUR_API_KEY"}
payload = {"klineType": 8, "klineNum": 10, "codes": "2330.TW"}
response = requests.post(url, headers=headers, json=payload)
print(response.json())返回示例:
{
"ret": 200,
"msg": "success",
"traceId": "414f11b1-6c5f-43bc-a6fe-e3b839018bc0",
"data": [
{
"s": "2330.TW",
"respList": [
{
"t": "1793808000",
"h": "2285.00",
"o": "2250.00",
"l": "2245.00",
"c": "2265.00",
"v": "17535000",
"vw": "39690465000",
"pc": "0.22%",
"pca": "5.00"
}
]
}
]
}| 字段 | 说明 |
|---|---|
t | K线时间,秒级时间戳(字符串) |
o / h / l / c | 开盘价 / 最高价 / 最低价 / 收盘价 |
v | 成交量(股) |
vw | 成交额 |
pc | 涨跌幅 |
pca | 涨跌额 |
返回结果按时间从新到旧排列。如果需要更早的数据,可以把本页最早一根K线的t减 1,作为下一次请求的timestamp:
import time
import requests
url = "https://data.infoway.io/taiwan/v2/batch_kline"
headers = {"apiKey": "YOUR_API_KEY"}
payload = {"klineType": 8, "klineNum": 500, "codes": "2330.TW"}
first_page = requests.post(url, headers=headers, json=payload).json()
candles = first_page["data"][0]["respList"]
time.sleep(1) # 免费试用每秒最多请求 1 次
payload["timestamp"] = int(candles[-1]["t"]) - 1
second_page = requests.post(url, headers=headers, json=payload).json()
candles += second_page["data"][0]["respList"]
print(f"共 {len(candles)} 根K线")4.4 查询一档盘口
请求地址:GET https://data.infoway.io/taiwan/batch_depth/{codes}
import requests
url = "https://data.infoway.io/taiwan/batch_depth/2330.TW"
headers = {"apiKey": "YOUR_API_KEY"}
response = requests.get(url, headers=headers)
depth = response.json()["data"][0]
print(f"卖一:{depth['a'][0][0]},数量 {depth['a'][1][0]}")
print(f"买一:{depth['b'][0][0]},数量 {depth['b'][1][0]}")返回示例:
{
"ret": 200,
"msg": "success",
"traceId": "59462e85-1c92-433c-b2de-464d9922a6ef",
"data": [
{
"s": "2330.TW",
"t": 1793847512608,
"a": [["2270.00"], ["208000.0"]],
"b": [["2265.00"], ["169000.0"]]
}
]
}a是卖盘,b是买盘。每一侧都包含两个数组,第一个是价格,第二个是挂单数量。
4.5 通过 WebSocket 订阅实时行情
如果要做实时看盘或者策略信号,建议使用 WebSocket。连接建立后,服务器会持续推送最新行情,不需要反复轮询。
台股订阅地址:wss://data.infoway.io/ws?business=taiwan&apikey=YOUR_API_KEY
| 数据类型 | 订阅协议号 | 应答协议号 | 推送协议号 |
|---|---|---|---|
| 实时成交 | 10000 | 10001 | 10002 |
| 实时盘口 | 10003 | 10004 | 10005 |
| 实时K线 | 10006 | 10007 | 10008 |
连接后需要每 30 秒发送一次心跳(协议号 10010)。下面的示例同时订阅台积电的成交、盘口和 1 分钟K线,并包含心跳和断线重连:
import asyncio
import json
import uuid
import websockets
API_KEY = "YOUR_API_KEY"
WS_URL = f"wss://data.infoway.io/ws?business=taiwan&apikey={API_KEY}"
CODES = "2330.TW"
def build_message(code, data=None):
message = {"code": code, "trace": uuid.uuid4().hex}
if data is not None:
message["data"] = data
return json.dumps(message)
async def subscribe(ws):
await ws.send(build_message(10000, {"codes": CODES}))
await ws.send(build_message(10003, {"codes": CODES}))
await ws.send(build_message(10006, {"arr": [{"type": 1, "codes": CODES}]}))
async def send_heartbeat(ws):
while True:
await asyncio.sleep(30)
await ws.send(build_message(10010))
def handle_message(message):
code = message.get("code")
data = message.get("data")
if code == 10002:
print(f"成交 {data['s']}:价格 {data['p']},数量 {data['v']}")
elif code == 10005:
print(f"盘口 {data['s']}:卖一 {data['a'][0][0]},买一 {data['b'][0][0]}")
elif code == 10008:
print(f"K线 {data['s']}:时间 {data['t']},最新价 {data['c']}")
else:
print(f"其他消息:{message}")
async def main():
while True:
try:
async with websockets.connect(WS_URL) as ws:
print("连接成功")
await subscribe(ws)
heartbeat = asyncio.create_task(send_heartbeat(ws))
try:
async for raw in ws:
handle_message(json.loads(raw))
finally:
heartbeat.cancel()
except (OSError, websockets.exceptions.WebSocketException) as error:
print(f"连接异常:{error}")
print("连接已断开,10 秒后重连")
await asyncio.sleep(10)
if __name__ == "__main__":
asyncio.run(main())每次重连都要重新发送订阅,上面的示例已经包含了这一步。
4.6 查询基础信息、交易日和财务报表
这些接口都使用https://data.infoway.io/common/basic开头的地址,请求方式都是 GET:
import time
import requests
base_url = "https://data.infoway.io/common/basic"
headers = {"apiKey": "YOUR_API_KEY"}
# 基础信息:名称、交易所、币种、每股盈利、板块等
info = requests.get(
f"{base_url}/symbols/info",
headers=headers,
params={"type": "STOCK_TW", "symbols": "2330.TW"},
)
print(info.json())
time.sleep(1) # 免费试用每秒最多请求 1 次
# 交易日历
trading_days = requests.get(
f"{base_url}/markets/trading_days",
headers=headers,
params={"market": "TW", "beginDay": "20260901", "endDay": "20260930"},
)
print(trading_days.json())
time.sleep(1)
# 季度利润表
income = requests.get(
f"{base_url}/financial/income_statement",
headers=headers,
params={"symbol": "2330.TW", "type": "STOCK_TW", "period_type": "fq"},
)
print(income.json())在基础信息的返回结果中,exchange为TWSE表示上市股票,为TPEX表示上柜股票。公司名称建议读取name_local(繁体中文)或name_en(英文),台股的name_cn目前为空。
5. 开发时的注意事项
- K线只能用 POST:部分市场还保留 GET 入口,但台股只支持
POST /taiwan/v2/batch_kline。 - WebSocket 的 business 要写对:台股必须使用
business=taiwan。如果 business 和品种不匹配,订阅可能显示成功,却收不到数据。 - 时间戳单位不同:成交和盘口的
t是毫秒整数,K线的t是秒级字符串。 - K线涨跌幅的字段名不同:HTTP 返回
pc,WebSocket 推送pfr。 - 实时K线要按键更新:同一根K线会被多次推送,且没有表示已收盘的字段。建议以
s、ty、t作为唯一键更新记录,不要每次都新增。 msg: ok不代表已有数据:订阅应答只说明请求已被接收。非交易时段收不到推送,是正常现象。- 控制 WebSocket 请求频率:每个连接每分钟最多发送 60 次订阅、取消订阅和心跳,超出后连接会被断开。
6. 常见问题
6.1 免费试用可以查台股吗?
可以。注册后有 7 天免费试用,无需绑定信用卡。试用期内,台股可以查询2330.TW(台积电),数据同样是实时的。如果请求中包含试用范围外的标的,查询会返回无权限。
6.2 不同套餐有什么区别?
| 套餐 | HTTP 频率 | WebSocket 订阅数 | WebSocket 连接数 |
|---|---|---|---|
| 免费试用 | 1 次/秒 | 10 | 1 |
| 基础 | 2 次/秒 | 200 | 1 |
| 高级 | 10 次/秒 | 800 | 2 |
| 专业 | 20 次/秒 | 5000 | 9 |
| 股票市场套餐 | 20 次/秒 | 所选市场的全部股票 | 自动分配 |
具体价格和历史数据范围请查看价格页。所有套餐都以 USDT 计价和支付。
6.3 上柜股票也能查吗?
可以。上市和上柜股票使用相同的接口和.TW后缀,比如上柜股票环球晶的代码是6488.TW。
6.4 支持台湾 ETF 吗?
暂不支持。当前的产品清单以上市和上柜股票为主,另有少量 REITs(如01001T.TW),0050.TW这类 ETF 不在清单中。具体标的请以最新的产品清单为准。
6.5 盘口能查到几档?
目前台股提供一档盘口,也就是买一和卖一的价格与数量。
6.6 新上市的股票能查到吗?
可以。产品清单会定期更新,新增新上市的股票,并移除已退市的股票。
6.7 还有其他问题怎么办?
完整的接口说明可以查看 Infoway API 文档,也可以通过 Telegram 联系我们的客服。
7. 其他支持的市场
除了台湾,Infoway还支持以下股票市场: