最近中东局势升温,原油直接跳涨——布伦特突破108美元,WTI站上103,国内SC原油更是一天涨了11%冲破900元大关。这种波动率下,做期货数据监控的人最关心的不是最新价一个数字,而是买卖盘口的厚度变化:大单托底还是压盘,多空力量在哪个价位堆积。
做量化或者行情工具开发的人都知道,期货数据接口跟股票接口看起来差不多,但实际接入时有几个关键差异。这篇从技术角度记录一下用Python接入期货行情API的完整过程,重点讲实时报价和五档盘口数据怎么拿、怎么解析、怎么用。
期货行情API与股票接口的核心差异
接之前先搞清楚几个差异,不然容易照搬股票的写法踩坑。
路径是 /future/ 单数形式。 跟股票 /stock/ 一样,别写成 /futures/。这个我第一次接的时候就404了,查了半天才发现。
市场代码用 US、HK、CN。 美国商品期货(GC黄金、CL原油、ZS大豆)用 US,国内期货用 CN。这个跟指数接口用 GB 不一样,别搞混了。
期货多了盘口depth数据。 股票接口虽然也支持depth,但期货的盘口分析更常见——因为期货是撮合交易,买卖盘口直接反映了流动性和多空力量。做短线策略的话,光看最新价根本不够。
K线周期多了2小时和4小时。 股票和指数的kType到1小时就是5,然后直接跳到日K(8)。期货多了 kType=6(2小时)和 kType=7(4小时),这是因为期货交易时段长,有些策略用2小时K线做中线判断。
REST API:历史K线与实时报价快照
先从基础的REST接口开始。拉一个商品期货的报价快照,调用方式跟其他产品类似:
import requests
API_BASE = "https://api.itick.org"
TOKEN = "your_token_here"
headers = {
"accept": "application/json",
"token": TOKEN
}
def get_future_quote(region, code):
"""
获取期货实时报价快照
region: US(美盘) CN(国内) HK(港股相关)
code: 期货合约代码,如 GC(黄金) CL(原油) ZS(大豆)
"""
resp = requests.get(
f"{API_BASE}/future/quote",
headers=headers,
params={"region": region, "code": code}
)
resp.raise_for_status()
return resp.json()["data"]
# 看一下美盘黄金和原油的快照
for region, code, name in [("US", "GC", "黄金"), ("US", "CL", "原油")]:
q = get_future_quote(region, code)
print(f"{name}({code}): 最新={q['ld']} 涨跌幅={q['chp']}% "
f"高={q['h']} 低={q['l']} 量={q['v']}")
报价字段跟其他产品基本一致:ld最新价、chp涨跌幅、o/h/l开高低、v成交量、p前收。做行情看板的时候,这些字段够用了。
K线接口也类似,但注意kType多了两个周期:
def get_future_kline(region, code, k_type=2, limit=50):
"""
k_type: 2=5分钟 5=1小时 6=2小时 7=4小时 8=日K
(期货比股票多了6=2小时和7=4小时两个周期)
"""
resp = requests.get(
f"{API_BASE}/future/kline",
headers=headers,
params={
"region": region,
"code": code,
"kType": k_type,
"limit": limit
}
)
resp.raise_for_status()
return resp.json()["data"]
# 拉原油5分钟K线,看今天波动有多大
cl_kline = get_future_kline("US", "CL", k_type=2, limit=50)
# 计算今天的振幅
today_range = max(bar["h"] for bar in cl_kline) - min(bar["l"] for bar in cl_kline)
print(f"近50根5分钟K线区间振幅: {today_range:.2f}")
原油这种品种波动大,用5分钟K线比日K更能捕捉盘中异动。kType=2就是5分钟,拉最近50根大概覆盖4个多小时的交易。做日内策略回测的时候,这个粒度刚好。
WebSocket实时推送:五档盘口数据接入
REST只能拿到静态快照,盘中盘口变化很快,必须用WebSocket。期货WebSocket的地址是 wss://api.itick.org/future,鉴权流程跟其他产品一样——先等 resAc:"auth" 再订阅。
关键区别在订阅类型。除了常规的 quote,期货还可以订阅 depth(盘口):
import json
import threading
import time
import websocket
WS_URL = "wss://api.itick.org/future"
TOKEN = "your_token_here"
authenticated = False
def start_heartbeat(ws):
def ping():
while True:
time.sleep(30)
ws.send(json.dumps({
"ac": "ping",
"params": str(int(time.time() * 1000))
}))
threading.Thread(target=ping, daemon=True).start()
def subscribe(ws):
# 同时订阅quote和depth两个类型
# GC=黄金 CL=原油,region=US
ws.send(json.dumps({
"ac": "subscribe",
"params": "GC$US,CL$US",
"types": "quote,depth"
}))
注意 types 参数传了两个值:quote 和 depth,用逗号分隔。这样同一个连接里既能收到最新价推送,也能收到买卖盘口变化。
盘口消息的数据结构跟报价完全不同,是一个数组:
def on_message(ws, message):
global authenticated
payload = json.loads(message)
if payload.get("resAc") == "auth" and payload.get("code") == 1 and not authenticated:
authenticated = True
subscribe(ws)
start_heartbeat(ws)
return
if payload.get("resAc") == "pong":
return
data = payload.get("data") or {}
# 报价消息:最新价
if data.get("type") == "quote":
print(f"[报价] {data['s']}: {data['ld']} ({data['chp']}%)")
# 盘口消息:买卖五档
elif data.get("type") == "depth":
bids = data.get("b", []) # 买盘
asks = data.get("a", []) # 卖盘
print(f"\n[盘口] {data['s']}")
for bid in bids:
print(f" 买{bid['po']}: {bid['p']} 量={bid['v']}")
for ask in asks:
print(f" 卖{ask['po']}: {ask['p']} 量={ask['v']}")
盘口数据的结构:b 是买盘数组,a 是卖盘数组。每个元素有三个字段:
po:盘口档位(1=买一/卖一,2=买二/卖二...)p:挂单价格v:挂单数量
这个数据做什么用?一个常见的分析是计算买卖盘力量比——把买盘总量加起来跟卖盘总量比,看哪边更厚。
盘口数据分析:买卖盘力量比计算
盘口原始数据就是一堆挂单,得加工一下才有意义。一个简单的指标是买卖盘总量比:
def analyze_depth(data):
"""
分析盘口数据,返回买卖盘力量对比
"""
bids = data.get("b", [])
asks = data.get("a", [])
bid_volume = sum(b["v"] for b in bids)
ask_volume = sum(a["v"] for a in asks)
if ask_volume == 0:
ratio = float("inf")
else:
ratio = bid_volume / ask_volume
# 买一卖一价差
spread = asks[0]["p"] - bids[0]["p"] if bids and asks else 0
return {
"bid_total": bid_volume,
"ask_total": ask_volume,
"bid_ask_ratio": round(ratio, 2),
"spread": spread,
"top_bid": bids[0]["p"] if bids else None,
"top_ask": asks[0]["p"] if asks else None,
}
# 在on_message里调用
elif data.get("type") == "depth":
stats = analyze_depth(data)
print(f" {data['s']} 买卖比={stats['bid_ask_ratio']} "
f"价差={stats['spread']} "
f"买一={stats['top_bid']} 卖一={stats['top_ask']}")
这个指标很直观:买卖比大于1说明买盘挂单更厚,下方支撑强;小于1说明卖盘压力大。价差(spread)小说明流动性好,价差大说明交易不活跃。
像最近原油这种暴涨行情,盘口数据特别有参考价值——如果卖盘很厚但价格还在涨,说明上方有阻力但买盘更激进;如果买盘在价格上涨过程中持续撤单,那可能是拉高出货。这些光看最新价是看不出来的。
完整实现:商品期货实时行情监控脚本
把报价和盘口拼起来,就是一个完整的期货监控脚本:
import json, threading, time, requests, websocket
API_BASE = "https://api.itick.org"
WS_URL = "wss://api.itick.org/future"
TOKEN = "your_token_here"
headers = {"accept": "application/json", "token": TOKEN}
watchlist = ["GC", "CL", "ZS"] # 黄金、原油、大豆
# 启动时拉快照
def init_quotes():
print("=== 商品期货快照 ===")
for code in watchlist:
r = requests.get(f"{API_BASE}/future/quote",
headers=headers,
params={"region": "US", "code": code}).json()["data"]
print(f" {code}: {r['ld']} ({r['chp']}%)")
# WebSocket实时订阅报价+盘口
authenticated = False
def on_message(ws, message):
global authenticated
payload = json.loads(message)
if payload.get("resAc") == "auth" and payload.get("code") == 1 and not authenticated:
authenticated = True
ws.send(json.dumps({
"ac": "subscribe",
"params": ",".join(f"{c}$US" for c in watchlist),
"types": "quote,depth"
}))
threading.Thread(target=heartbeat, args=(ws,), daemon=True).start()
return
data = payload.get("data") or {}
if data.get("type") == "quote":
print(f"[报价] {data['s']}: {data['ld']} ({data['chp']}%)")
elif data.get("type") == "depth":
bv = sum(b["v"] for b in data.get("b", []))
av = sum(a["v"] for a in data.get("a", []))
ratio = round(bv / av, 2) if av else 0
print(f"[盘口] {data['s']}: 买卖比={ratio} 买盘={bv} 卖盘={av}")
def heartbeat(ws):
while True:
time.sleep(30)
ws.send(json.dumps({"ac": "ping", "params": str(int(time.time() * 1000))}))
init_quotes()
ws = websocket.WebSocketApp(WS_URL, header=[f"token: {TOKEN}"], on_message=on_message)
ws.run_forever()
这个脚本跑起来之后,开盘前先看到三个品种的快照,盘中同时收到报价和盘口推送。盘口消息的频率比报价更高——每次挂单变化都会推,所以实际跑起来depth消息会很多,如果只关心报价可以只订 quote 不订 depth。
接入注意事项与常见问题
盘口消息频率很高,注意限流。 期货挂单变化频繁,depth推送可能一秒好几条。如果在on_message里做了重计算(比如调外部API),很容易跟不上推送速度。建议把depth数据存到一个ring buffer里,用单独的线程去分析,不要在WebSocket回调里做重活。
kType多了6和7。 期货支持2小时K线(kType=6)和4小时K线(kType=7),这是股票和指数没有的。做隔夜趋势分析的时候4小时K线比日K更细腻。
国内期货用region=CN。 上面例子用的是美盘品种(GC、CL),如果要接国内期货比如螺纹钢、铁矿石,region传 CN,代码格式需要查一下文档里的品种列表。
盘口数据的档位深度取决于套餐。 不是所有套餐都能拿到完整五档盘口,有些基础套餐可能只有买一卖一。写代码的时候别假设一定有五档,遍历的时候用 for bid in bids 而不是按下标取。
期货有涨跌停板。 ts 字段为3的时候是熔断/涨跌停,这时候盘口数据可能出现单边挂单——涨停板上全是卖单没人买,或者跌停板上全是买单。分析买卖比的时候要考虑这种极端情况。
总结
期货行情接入的核心思路跟其他产品差不多:REST做初始化和历史数据,WebSocket做实时推送。但期货有两个独特的价值点:一是盘口depth数据,买卖五档挂单直接反映多空力量;二是多了2小时和4小时K线周期,适合做中线趋势分析。
最近原油因为地缘政治暴涨,波动率拉满,这种时候盘口数据的价值比平时更高。有一套自己写的监控脚本,比看行情软件上密密麻麻的五档数字要直观得多。
参考文档:https://docs.itick.org/websocket/future
GitHub:https://github.com/itick-org/
