2021 年 1 月,GameStop(GME)逼空行情在美股盘后继续发酵。交易所已收盘,散户只能盯着隔夜期货猜测走势;而持有 GMEUSDT 股票代币的交易者,却能在链上继续买卖,捕捉盘后的价格发现机会。
这就是股票代币的核心价值:把传统股票搬到区块链上,让它像加密货币一样 24 小时不间断交易。对于想要构建全天候行情监控或交易系统的开发者来说,股票代币行情接口是一个值得关注的新数据品类。
一、什么是股票代币
股票代币(Tokenized Stocks)是由链上机构发行的、锚定真实上市公司股价的数字资产。每枚代币对应一定比例的真实股票,价格在美股交易时段与标的股票高度联动,盘后则由链上供需决定。
与传统股票行情接口相比,股票代币行情有几个显著特点:
- 7×24 小时连续交易:不受交易所开盘时间限制,盘前、盘后、节假日均有成交
- USDT 计价:价格以 USDT 结算,无需换汇
- 盘口为一档:股票代币的买卖盘口只提供最优一档(买一/卖一),不同于 A 股、港股的五档或十档
- 覆盖股票 + ETF + 贵金属:除个股外,还包括 SPYUSDT(标普 500 ETF)、QQQUSDT(纳斯达克 100 ETF)、XAUUSDT(现货黄金)等
二、股票代币品种
Infoway API 目前支持64 个股票代币,覆盖科技、金融、能源、消费等主要板块,同时包含主流 ETF 和贵金属:
科技股:AAPLUSDT(苹果)、NVDAUSDT(英伟达)、MSFTUSDT(微软)、GOOGLUSDT(谷歌)、METAUSDT(Meta)、TSLAUSDT(特斯拉)、AMDUSDT(AMD)、INTCUSDT(英特尔)、QCOMUSDT(高通)、TSMUSDT(台积电)、ASML(阿斯麦)…
金融股:JPMUSDT(摩根大通)、BACUSDT(美国银行)、BRKBUSDT(伯克希尔 B 类)、COINUSDT(Coinbase)、HOODUSDT(Robinhood)…
能源 / 消费:CVXUSDT(雪佛龙)、OXYUSDT(西方石油)、AMZNUSDT(亚马逊)、COSTUSDT(好市多)、NFLXUSDT(Netflix)…
ETF:SPYUSDT(标普 500)、QQQUSDT(纳斯达克 100)、SOXLUSDT(三倍做多半导体)、EWJUSDT(MSCI 日本)、EWYUSDT(MSCI 韩国)
贵金属:XAUUSDT(现货黄金)、XAGUSDT(现货白银)、XCUUSDT(现货铜)、XPTUSDT(铂金)、XPDUSDT(钯金)
通过接口获取完整列表:
importrequests API_KEY="your_api_key"BASE_URL="https://data.infoway.io"resp=requests.get(f"{BASE_URL}/common/basic/symbols",headers={"apiKey":API_KEY},params={"type":"CRYPTO"})all_symbols=resp.json()["data"]# 股票代币的 symbol 均以 USDT 结尾,过滤即可token_stocks=[sforsinall_symbolsifs["symbol"].endswith("USDT")]print(f"股票代币品种数量:{len(token_stocks)}")三、REST API 快速上手
股票代币与加密货币共用同一套接口路径,business=crypto即可访问。
3.1 实时成交明细
defget_token_trades(symbols:list[str])->list[dict]:"""查询股票代币最新成交明细"""codes=",".join(symbols)resp=requests.get(f"{BASE_URL}/crypto/batch_trade/{codes}",headers={"apiKey":API_KEY})resp.raise_for_status()returnresp.json()["data"]trades=get_token_trades(["TSLAUSDT","NVDAUSDT","AAPLUSDT"])fortintrades:direction={0:"中性",1:"买入",2:"卖出"}.get(t["td"],"未知")print(f"{t['s']}: 价格={t['p']}USDT 成交量={t['v']}股 方向={direction}")注意:
v字段单位为股票份额数(股),而非"手"。v="6.251"表示成交 6.251 股 AAPL 代币。
3.2 实时买卖盘口(一档)
股票代币的盘口只有最优一档,返回结构与多档盘口相同,但数组长度为 1:
defget_token_depth(symbols:list[str])->list[dict]:"""查询股票代币一档买卖盘口"""codes=",".join(symbols)resp=requests.get(f"{BASE_URL}/crypto/batch_depth/{codes}",headers={"apiKey":API_KEY})resp.raise_for_status()returnresp.json()["data"]depth_list=get_token_depth(["TSLAUSDT","PDDUSDT"])foritemindepth_list:ask_price=item["a"][0][0]# 卖一价ask_vol=item["a"][1][0]# 卖一量bid_price=item["b"][0][0]# 买一价bid_vol=item["b"][1][0]# 买一量spread=float(ask_price)-float(bid_price)print(f"{item['s']}: 卖一={ask_price}({ask_vol}股) 买一={bid_price}({bid_vol}股) 价差={spread:.5f}")3.3 K 线数据
importtimedefget_token_kline(symbol:str,kline_type:int=1,num:int=100)->list[dict]:""" 获取股票代币 K 线 kline_type: 1=1分钟, 5=1小时, 8=日K """resp=requests.post(f"{BASE_URL}/crypto/v2/batch_kline",headers={"apiKey":API_KEY},json={"klineType":kline_type,"klineNum":num,"codes":symbol})resp.raise_for_status()data=resp.json()["data"]returndata[0]["respList"]ifdataelse[]# 获取特斯拉代币最近 50 根 1 小时 K 线klines=get_token_kline("TSLAUSDT",kline_type=5,num=50)forkinklines[:5]:ts=time.strftime("%m-%d %H:%M",time.localtime(int(k["t"])))print(f"{ts}| O:{k['o']}H:{k['h']}L:{k['l']}C:{k['c']}涨跌:{k['pc']}")四、WebSocket 实时订阅(含断线重连)
股票代币使用business=crypto的 WebSocket 地址:
wss://data.infoway.io/ws?business=crypto&apikey=YOUR_API_KEY以下是完整 Python 客户端,同时订阅 TSLA、NVDA、AAPL 三只代币的成交明细 + 盘口 + 1 分钟 K 线:
importos,asyncio,json,uuid,loggingfromtypingimportOptionalimportwebsocketsfromwebsockets.asyncio.clientimportClientConnectionfromwebsockets.exceptionsimportConnectionClosed logging.basicConfig(level=logging.INFO,format="%(asctime)s - %(levelname)s - %(message)s")logger=logging.getLogger("tokenstock-ws")REQ_TRADE=10000REQ_DEPTH=10003REQ_KLINE=10006REQ_HEARTBEAT=10010PUSH_TRADE,PUSH_DEPTH,PUSH_KLINE=10002,10005,10008ACK_CODES={10001,10004,10007}SUBSCRIBE_SYMBOLS="TSLAUSDT,NVDAUSDT,AAPLUSDT"classTokenStockWSClient:"""股票代币行情 WebSocket 客户端(指数退避重连)"""def__init__(self,api_key:str):self.ws_url=f"wss://data.infoway.io/ws?business=crypto&apikey={api_key}"self.ws:Optional[ClientConnection]=Noneself.running=Trueself.reconnect_base,self.reconnect_max=5,60self.heartbeat_interval=30self.heartbeat_task:Optional[asyncio.Task]=Noneasyncdef_send(self,msg:dict)->None:awaitself.ws.send(json.dumps(msg))asyncdef_subscribe_all(self)->None:t=lambda:str(uuid.uuid4())awaitself._send({"code":REQ_TRADE,"trace":t(),"data":{"codes":SUBSCRIBE_SYMBOLS}})awaitself._send({"code":REQ_DEPTH,"trace":t(),"data":{"codes":SUBSCRIBE_SYMBOLS}})awaitself._send({"code":REQ_KLINE,"trace":t(),"data":{"arr":[{"type":1,"codes":SUBSCRIBE_SYMBOLS}]}})logger.info("已订阅股票代币:%s",SUBSCRIBE_SYMBOLS)def_start_heartbeat(self)->None:self._cancel_heartbeat()asyncdef_loop():try:whileTrue:awaitasyncio.sleep(self.heartbeat_interval)ifself.wsisNoneorself.ws.close_codeisnotNone:breakawaitself._send({"code":REQ_HEARTBEAT,"trace":str(uuid.uuid4())})except(ConnectionClosed,asyncio.CancelledError):passself.heartbeat_task=asyncio.create_task(_loop())def_cancel_heartbeat(self)->None:ifself.heartbeat_taskandnotself.heartbeat_task.done():self.heartbeat_task.cancel()self.heartbeat_task=Nonedef_on_message(self,raw:str)->None:try:msg=json.loads(raw)exceptjson.JSONDecodeError:returncode,data=msg.get("code"),msg.get("data",{})ifcode==PUSH_TRADE:direction={1:"买入",2:"卖出"}.get(data.get("td"),"中性")logger.info("[成交] %s 价格=%s USDT 量=%s股 方向=%s",data.get("s"),data.get("p"),data.get("v"),direction)elifcode==PUSH_DEPTH:# 股票代币只有一档,取 [0][0] 即可ask1=data["a"][0][0]ifdata.get("a")else"N/A"bid1=data["b"][0][0]ifdata.get("b")else"N/A"logger.info("[盘口] %s 卖一=%s 买一=%s",data.get("s"),ask1,bid1)elifcode==PUSH_KLINE:logger.info("[K线] %s 收=%s USDT 涨跌=%s",data.get("s"),data.get("c"),data.get("pfr"))elifcodeinACK_CODES:logger.info("订阅确认 code=%s",code)asyncdef_connect_once(self)->None:asyncwithwebsockets.connect(self.ws_url)asws:self.ws=ws logger.info("WebSocket 连接成功")awaitself._subscribe_all()self._start_heartbeat()try:asyncformessageinws:self._on_message(message)finally:self._cancel_heartbeat()self.ws=Noneasyncdefstart(self)->None:backoff=self.reconnect_basewhileself.running:try:awaitself._connect_once()backoff=self.reconnect_baseexceptConnectionClosedase:logger.warning("连接关闭: %s",e)exceptExceptionase:logger.error("连接异常: %s",e)ifnotself.running:breaklogger.info("%.0f 秒后重连...",backoff)awaitasyncio.sleep(backoff)backoff=min(backoff*2,self.reconnect_max)asyncdefmain():api_key=os.environ.get("INFOWAY_API_KEY","YOUR_API_KEY")awaitTokenStockWSClient(api_key).start()if__name__=="__main__":try:asyncio.run(main())exceptKeyboardInterrupt:print("退出")五、使用注意事项
5.1 盘口只有一档
股票代币的深度数据中,a(卖盘)和b(买盘)数组长度均为1,直接取[0][0]即可,无需像股票接口那样遍历多档。
5.2 美股交易时段 vs 盘后时段的行为差异
美股开盘期间(北京时间 21:30–04:00,夏令时提前 1 小时),代币价格与标的股票联动紧密,价差极小;盘后时段流动性下降,价差可能扩大。构建策略时建议用时间段过滤:
importdatetime,pytzdefis_us_market_open()->bool:"""判断当前是否处于美股正式交易时段(含夏令时自动切换)"""ny_tz=pytz.timezone("America/New_York")now_ny=datetime.datetime.now(ny_tz)ifnow_ny.weekday()>=5:# 周末returnFalsemarket_open=now_ny.replace(hour=9,minute=30,second=0,microsecond=0)market_close=now_ny.replace(hour=16,minute=0,second=0,microsecond=0)returnmarket_open<=now_ny<=market_close5.3 跨接口联动:获取标的公司财报数据
股票代币的底层是真实上市公司,可通过财报接口查询对应股票的基本面数据,Symbol 格式切换回标准股票代码即可:
defget_underlying_financials(token_symbol:str)->dict:"""通过代币 Symbol(如 TSLAUSDT)查询标的股票财务数据"""ticker=token_symbol.replace("USDT","")# TSLAUSDT → TSLAus_symbol=f"{ticker}.US"# → TSLA.USresp=requests.get(f"{BASE_URL}/common/basic/financial/statistics",headers={"apiKey":API_KEY},params={"symbol":us_symbol,"type":"STOCK_US"})resp.raise_for_status()returnresp.json()["data"]stats=get_underlying_financials("NVDAUSDT")foriteminstats[:3]:print(f"{item['itemName']}:{item.get('currentValue')}")5.4 ETF 和贵金属代币的特殊性
SPYUSDT、QQQUSDT等 ETF 代币没有对应的个股财报接口;XAUUSDT(黄金)、XAGUSDT(白银)等贵金属代币同样不支持财报查询,但可通过/common/batch_trade/获取实时报价,逻辑与黄金现货行情一致。