大宗商品行情API接入教程

发布时间:2026/7/31 20:55:36

大宗商品行情API接入教程 股票的世界很规整一只股票只在一个交易所撮合每天九点半开盘、下午收盘收盘后你尽可以从容跑日终任务。可原油、黄金、天然气这些大宗商品完全不是这个节奏它们几乎全天候在交易周一到周五近乎 24 小时不停只在周末休市更麻烦的是交易时段还会随着欧美的夏令时、冬令时来回平移一小时。你按股票的思路写个「北京时间下午三点抓收盘价」的定时任务放到黄金上直接就废了因为那个时刻黄金正在剧烈交易根本没有收盘价这回事。再加上大宗商品之间盘根错节的联动原油涨、通胀预期升、黄金跟着动美元走强、以美元计价的商品普遍承压想做一点像样的分析你得同时、实时地盯住好几个品种。这篇文章就讲清楚大宗商品实时行情到底该怎么接原油、黄金、天然气的代码怎么写交易时段和夏令时这些坑怎么绕以及怎么用一套接口把它们和汇率、股指放在一起统一处理。一、大宗商品行情和股票行情差在哪动手写代码前先把这个市场的三个「反直觉」特征搞清楚后面的设计才不会跑偏。第一没有统一的「开盘收盘」近乎全天交易。大宗商品尤其是以差价合约 / 现货形式报价的原油、贵金属在全球市场接力交易工作日几乎 24 小时连续。它不像股票有明确的收盘价也不像加密货币那样连周末都不停它是「工作日近乎全天 周末休市」的混合节奏。这决定了你的采集程序既要能长时间在线又要能正确识别周末与节假日的休市空窗。第二交易时段随夏令时平移。因为报价时区锚定在 GMT而欧美每年在三月、十一月切换夏令时/冬令时大宗商品的每日交易起止时间会整体前移或后移一小时。比如美原油夏令时是周日 22:00 开始到周五 21:00GMT冬令时则变成周日 23:00 到周五 22:00。如果你把交易时段硬编码成固定的 UTC 小时一年里就有两段时间会判断错误。第三品种之间强联动需要「一把抓」。分析原油离不开天然气、离不开美元指数分析黄金要看白银金银比、看实际利率。所以大宗商品的数据接入几乎天然就是多品种、跨类别的需求你很少只盯一个品种。正因如此与其分别去对接不同的商品交易所或数据源不如用一个把能源、贵金属、外汇、期货统一成同一套格式的行情 API一次请求就把一篮子品种全查回来。下面用一套真实可跑的接口演示。二、接口概览能源、贵金属、期货同一套接口我们以 Infoway API 的接口为例。它的一个便利之处是股票、加密货币走各自的专用前缀而外汇、能源、贵金属、期货这几类「非股票」品种统一走/common/前缀WebSocket 则统一用businesscommon。也就是说你写一套代码就能同时拿原油、黄金和欧元汇率。常用的大宗商品代码可调用产品列表接口分别传typeENERGY、typeMETAL、typeFUTURES获取完整清单类别品种代码能源美原油WTIUSOIL/WTI能源布伦特原油UKOIL能源天然气NGAS贵金属黄金XAUUSD贵金属白银XAGUSD贵金属铂金 / 钯金XPTUSD/XPDUSD基本金属现货铜 / 镍 / 铅XCUUSD/XNIUSD/XPBUSD期货国际股指、国债、外汇期货等采用「主力连续合约」代码形如6E1!、10Y1!1!表示自动拼接的近月连续合约这里顺带说一个对期货用户很关键的点代码里1!后缀代表主力连续合约,数据源已经帮你把不同到期月份的合约按主力换月规则自动拼接好了。这意味着你不用自己处理「合约到期、移仓换月、价格跳空」这套让无数人头疼的展期逻辑直接拿连续序列做分析即可。三、REST 接口查实时价格、盘口与历史K线三个接口共用一个放在请求头里的apiKey。注意大宗商品全部走/common/路径。3.1 实时成交明细一次拿一篮子价格importrequests BASEhttps://data.infoway.ioHEADERS{apiKey:你的APIKey}defget_trades(codes):查询大宗商品最新成交明细codes 逗号分隔最多 100 个urlf{BASE}/common/batch_trade/{codes}resprequests.get(url,headersHEADERS,timeout10)resp.raise_for_status()returnresp.json()[data]# 一次把原油、黄金、天然气、白银全查回来foriteminget_trades(USOIL,XAUUSD,NGAS,XAGUSD):print(f{item[s]:8}价格{item[p]:12}成交量{item[v]:10}f时间{item[t]})返回的每条记录里s是品种代码t是毫秒时间戳p是最新价v是成交量vw是成交额。因为一次可以查上百个代码把相关联的品种放在同一次请求里既省配额又能保证它们的时间戳基本对齐方便做联动分析。3.2 盘口深度估算滑点defget_depth(codes):urlf{BASE}/common/batch_depth/{codes}resprequests.get(url,headersHEADERS,timeout10)resp.raise_for_status()returnresp.json()[data]fordinget_depth(XAUUSD):ask_px,bid_pxd[a][0],d[b][0]# 卖盘、买盘的价格数组best_ask,best_bidfloat(ask_px[0]),float(bid_px[0])print(f{d[s]}买一{best_bid}卖一{best_ask}f点差{best_ask-best_bid:.3f})盘口里a是卖盘、b是买盘各自是「价格数组 挂单量数组」两个平行数组下标一一对应。对大宗商品来说点差买一卖一之差是衡量流动性和交易成本的核心指标下单前用它估算滑点很有必要。3.3 历史K线主力连续合约的长序列回测和画图要用历史 K 线。接口是 POSTklineType指定周期11 分钟51 小时8日 K……klineNum指定数量。有一个必须记住的限制单个品种一次最多取 500 根但一次传多个品种时每个只返回最近 2 根。所以「批量查最新」和「拉深度历史」不可兼得,要深历史就得一个品种一个品种地翻页。importtimedefget_kline(code,kline_type8,num500,end_tsNone):body{klineType:kline_type,klineNum:num,codes:code}ifend_ts:body[timestamp]end_ts# 秒级时间戳向前翻历史urlf{BASE}/common/v2/batch_klineresprequests.post(url,headersHEADERS,jsonbody,timeout15)resp.raise_for_status()returnresp.json()[data][0][respList]deffetch_history(code,kline_type8,total2000):翻页拉取更长历史每页 500 根用最老一根的时间戳继续向前bars,end_ts[],Nonewhilelen(bars)total:pageget_kline(code,kline_type,500,end_ts)ifnotpage:breakbars.extend(page)end_tsint(page[-1][t])-1# respList 从新到旧取最老一根往前翻time.sleep(0.3)# 翻页间歇避免触发限流returnbars[:total]dailyfetch_history(XAUUSD,kline_type8,total2000)print(f黄金日K共{len(daily)}根最新收盘 {daily[0][c]})K 线返回是按时间从新到旧排列的翻页时把当前页最老一根的时间戳减 1 传给timestamp即可继续往历史深处走。要注意只有分钟 K、小时 K 支持timestamp向前翻页日 K 及以上周期不受此限制。因为代码用的是主力连续合约拉回来的历史序列是已经处理过换月的连续价格可以直接算指标、跑回测。四、实战交易时段判断与「金油比」联动监控把前面的接口拼起来来解决两个大宗商品特有的实际问题。4.1 正确判断「现在是不是交易时段」这是新手最容易栽的地方。以美原油为例它工作日近乎全天交易但每天有一个短暂的结算休市且交易起止时间随夏令时平移。与其自己硬编码时段更稳妥的做法是「用数据判断」如果最新成交的时间戳距现在超过了一个合理阈值比如几分钟就认为当前不在活跃交易时段。# 用真实数据来源时间做判断天然免疫夏令时切换——因为你不去猜时段# 而是看「最近一笔成交距现在有多久」。defis_market_active(code,stale_seconds300):最近一笔成交在 stale_seconds 内则认为处于活跃交易时段importtimeas_t dataget_trades(code)ifnotdata:returnFalselast_msdata[0][t]# 毫秒时间戳age_t.time()-last_ms/1000returnagestale_secondsprint(美原油当前是否在交易,is_market_active(USOIL))这个思路的妙处在于你完全不需要维护一张「夏令时几点到几点、冬令时几点到几点」的时段表也不用关心周末和节假日休市数据本身的新鲜度就是最可靠的信号。夏令时切换、临时休市、节假日全都自动兼容。4.2 监控「金油比」一个经典的大宗商品联动指标金油比黄金价格 ÷ 原油价格是宏观交易里常被拿来观察的比值它大致反映避险情绪与通胀预期的此消彼长。一次请求把两个品种查回来算比值即可。defgold_oil_ratio():data{d[s]:float(d[p])fordinget_trades(XAUUSD,USOIL)}gold,oildata[XAUUSD],data[USOIL]ratiogold/oilprint(f黄金{gold}原油{oil}金油比{ratio:.1f})returnratio gold_oil_ratio()因为两个价格来自同一次请求、时间戳几乎一致算出来的比值不会有「一个是刚才的价、一个是几分钟前的价」那种时间错配问题。要盯盘的话把它放进一个循环里定时跑或者干脆改用 WebSocket 订阅实时推送。五、WebSocket 实时订阅让一篮子商品持续推送要持续跟踪而不是查一次快照就该上 WebSocket。大宗商品走businesscommon。协议约定很清晰客户端用某协议号发订阅请求服务端用「请求号 1」确认、用「请求号 2」推送数据。数据订阅请求码确认码推送码成交明细100001000110002盘口深度100031000410005K 线100061000710008心跳10010—10010下面是一个生产可用的 Python 客户端重点做对三件事断线指数退避重连、定时心跳、以及重连后必须重新订阅。importasyncioimportjsonimportuuidimportloggingimportwebsocketsfromwebsockets.exceptionsimportConnectionClosed logging.basicConfig(levellogging.INFO,format%(asctime)s - %(levelname)s - %(message)s)loggerlogging.getLogger(commodity-ws)REQ_TRADE,REQ_DEPTH,REQ_KLINE,REQ_HEARTBEAT10000,10003,10006,10010PUSH_CONNECTED200PUSH_TRADE,PUSH_DEPTH,PUSH_KLINE10002,10005,10008ACK_CODES{10001,10004,10007}classCommodityWSClient:def__init__(self,api_key,symbolsUSOIL,XAUUSD,NGAS):# 大宗商品统一走 businesscommonself.urlfwss://data.infoway.io/ws?businesscommonapikey{api_key}self.symbolssymbols self.wsNoneself.runningTrueself.heartbeat_interval30self.reconnect_base,self.reconnect_max5,60asyncdef_send(self,msg):awaitself.ws.send(json.dumps(msg))asyncdef_subscribe_all(self):连接建立后、以及每次重连后都要重新订阅tlambda:str(uuid.uuid4())awaitself._send({code:REQ_TRADE,trace:t(),data:{codes:self.symbols}})awaitself._send({code:REQ_DEPTH,trace:t(),data:{codes:self.symbols}})awaitself._send({code:REQ_KLINE,trace:t(),data:{arr:[{type:1,codes:self.symbols}]}})logger.info(已订阅%s,self.symbols)asyncdef_heartbeat(self):try:whileTrue:awaitasyncio.sleep(self.heartbeat_interval)ifself.wsandself.ws.close_codeisNone:awaitself._send({code:REQ_HEARTBEAT,trace:str(uuid.uuid4())})except(ConnectionClosed,asyncio.CancelledError):passdef_dispatch(self,message):try:msgjson.loads(message)exceptjson.JSONDecodeError:returncode,datamsg.get(code),msg.get(data,{})ifcodePUSH_CONNECTED:logger.info(连接成功: %s,msg.get(msg))elifcodePUSH_TRADE:logger.info(成交: %s,data)elifcodePUSH_DEPTH:logger.info(盘口: %s,data)elifcodePUSH_KLINE:logger.info(K线: %s,data)elifcodeinACK_CODES:logger.info(订阅确认 code%s,code)asyncdef_connect_once(self):asyncwithwebsockets.connect(self.url)asws:self.wsws logger.info(WebSocket 已连接)awaitself._subscribe_all()# 关键连上立即订阅hbasyncio.create_task(self._heartbeat())try:asyncformessageinws:self._dispatch(message)finally:hb.cancel()self.wsNoneasyncdefstart(self):backoffself.reconnect_basewhileself.running:try:awaitself._connect_once()backoffself.reconnect_baseexceptExceptionase:logger.warning(连接断开: %s%s 秒后重连,e,backoff)awaitasyncio.sleep(backoff)backoffmin(backoff*2,self.reconnect_max)# 指数退避if__name____main__:clientCommodityWSClient(api_key你的APIKey,symbolsUSOIL,UKOIL,XAUUSD,XAGUSD,NGAS)asyncio.run(client.start())几个关键设计和大宗商品场景直接相关全程单连接 指数退避。断线后千万别开并行的重连任务一瞬间冒出好几条连接很容易被服务端当异常流量限流。始终只维护一条连接断了退避重连、退避时间翻倍封顶。重连后一定重新订阅。WebSocket 连接不携带订阅状态旧订阅随断线消失。每次建连都调_subscribe_all()否则会出现「连着但收不到数据」的怪象。周末休市不是故障。大宗商品周末不交易此时收不到推送是正常现象不要把它误判成连接故障而疯狂重连。可以用第四节的「数据新鲜度」思路来区分「休市」与「真断线」。六、常见问题FAQ原油、黄金这些大宗商品交易时间到底怎么算它们工作日近乎 24 小时连续交易、周末休市每日交易起止时间锚定 GMT并随欧美夏令时/冬令时平移一小时。最稳妥的做法不是硬编码时段表而是用「最近一笔成交距现在多久」来判断是否处于活跃交易时段天然兼容夏令时切换和节假日。为什么代码里带1!和普通合约有什么区别1!表示主力连续合约数据源已按主力换月规则把不同到期月份自动拼接成一条连续序列。你不用自己处理合约到期、移仓换月和跳空直接拿来做指标和回测即可。现货类品种如XAUUSD、USOIL则没有这个后缀。能同时拿原油、黄金、汇率、股指吗可以而且用同一套接口。外汇、能源、贵金属、期货都走/common/前缀WebSocket 用businesscommon一次请求最多可查上百个代码。做金油比、金银比、美元与商品联动这类分析特别方便。REST 和 WebSocket 怎么选要「此刻一个快照」如计算一次金油比、下单前确认价格用 REST 查一次要「持续实时流」盯盘、看板、策略用 WebSocket 订阅。别用高频轮询 REST 去模拟实时流既费配额又容易触发限流。历史数据能拉多深可回测吗用 K 线接口翻页拉取单品种每页 500 根靠时间戳向前翻可以取到很长的历史配合主力连续合约足够做回测。可回溯深度取决于套餐权限多品种同查时每个只返回最近 2 根深历史要逐个品种拉。时间戳精度要注意什么成交明细和盘口的时间戳t是毫秒级K 线的t是秒级两者混用会导致时间对不齐。做跨数据类型的对齐时记得先统一单位。大宗商品行情的接入难点不在于查一个价格而在于它那套「近乎全天交易、随夏令时平移、品种彼此联动」的独特节奏。把交易时段交给数据新鲜度去判断、把换月交给主力连续合约去处理、再用一套/common/接口把原油、黄金、天然气和汇率一并拿下剩下的精力就可以还给真正重要的分析与策略了。

相关新闻