请求期权链
reqSecDefOptParams() 用来请求某个标的的期权链参数。新手最容易卡住的地方是 underlyingConId,它不是股票代码,而是 IBKR 合约 ID。AAPL 股票常见返回的 conId 为 265598,实际项目应以 reqContractDetails() 返回为准。
app.reqSecDefOptParams( reqId, underlyingSymbol, futFopExchange, underlyingSecType, underlyingConId,)| 参数 | 类型 | 中文含义 | AAPL 示例 | 说明 |
|---|---|---|---|---|
reqId | int | 请求编号 | 9503 | 自己分配,用来把请求和回调对应起来。 |
underlyingSymbol | str | 标的代码 | "AAPL" | 股票代码、期货根代码等。 |
futFopExchange | str | 期货或期货期权交易所 | "" | 股票期权通常传空字符串;期货期权才常需要指定交易所。 |
underlyingSecType | str | 标的证券类型 | "STK" | 股票是 STK,期货是 FUT。 |
underlyingConId | int | 标的合约 ID | 265598 | 建议先用 reqContractDetails() 查出准确值。 |
请求期权链前,至少要确认三件事:
| 检查项 | 为什么重要 |
|---|---|
| TWS 或 IB Gateway 已启用 API | Socket 没开时 Python 无法连接。 |
标的 conId 已确认 | 只靠代码可能遇到歧义合约。 |
已收到 nextValidId | 说明 API 握手完成,可以发送请求。 |
Python 示例
Section titled “Python 示例”下面示例只输出数量和范围,避免把很长的到期日集合、行权价集合全部刷屏。
from __future__ import annotations
import threadingfrom collections import Counter
from ibapi.client import EClientfrom ibapi.wrapper import EWrapper
AAPL_CON_ID = 265598
class OptionChainApp(EWrapper, EClient): def __init__(self): EClient.__init__(self, self) self.ready = threading.Event() self.end = threading.Event() self.rows = [] self.errors = []
def nextValidId(self, orderId): # 收到这个回调,表示 TWS API 握手完成。 self.ready.set()
def error(self, reqId, *args): # 2104、2106、2158 通常是行情/数据农场连接提示,不算业务失败。 if len(args) >= 2: code = int(args[0]) msg = str(args[1]) else: code = -1 msg = str(args) if code not in {2104, 2105, 2106, 2158}: self.errors.append((reqId, code, msg))
def securityDefinitionOptionParameter( self, reqId, exchange, underlyingConId, tradingClass, multiplier, expirations, strikes, ): sorted_expirations = sorted(expirations) sorted_strikes = sorted(float(strike) for strike in strikes) self.rows.append( { "exchange": exchange, "underlyingConId": underlyingConId, "tradingClass": tradingClass, "multiplier": multiplier, "expirationCount": len(sorted_expirations), "strikeCount": len(sorted_strikes), "firstExpiration": sorted_expirations[0], "lastExpiration": sorted_expirations[-1], "minStrike": min(sorted_strikes), "maxStrike": max(sorted_strikes), } )
def securityDefinitionOptionParameterEnd(self, reqId): # 收到 End 才表示这次期权链参数已经返回完。 self.end.set()
app = OptionChainApp()
try: app.connect("127.0.0.1", 7497, clientId=9503) thread = threading.Thread(target=app.run, daemon=True) thread.start()
if not app.ready.wait(10): raise RuntimeError("等待 nextValidId 超时")
app.reqSecDefOptParams(9503, "AAPL", "", "STK", AAPL_CON_ID) app.end.wait(15)
exchange_counts = Counter(row["exchange"] for row in app.rows) print(f"OPTION_END_RECEIVED={app.end.is_set()}") print(f"OPTION_ROW_COUNT={len(app.rows)}") print("OPTION_EXCHANGE_COUNTS=" + ",".join( f"{exchange}:{count}" for exchange, count in sorted(exchange_counts.items()) ))
finally: if app.isConnected(): app.disconnect()示例连接返回:
CONNECTED=TrueOPTION_END_RECEIVED=TrueOPTION_ROW_COUNT=20OPTION_EXCHANGE_COUNTS=AMEX:1,BATS:1,BOX:1,CBOE:1,CBOE2:1,EDGX:1,EMERALD:1,GEMINI:1,IBUSOPT:1,ISE:1,MEMX:1,MERCURY:1,MIAX:1,NASDAQBX:1,NASDAQOM:1,PEARL:1,PHLX:1,PSE:1,SAPPHIRE:1,SMART:1NON_INFO_ERROR_COUNT=0为什么需要 underlyingConId
Section titled “为什么需要 underlyingConId”同一个代码在不同市场、不同产品类型下可能不是同一个合约。conId 能把标的唯一化,减少歧义。
股票期权的 futFopExchange 填什么
Section titled “股票期权的 futFopExchange 填什么”美股股票期权通常传空字符串 ""。这个参数名称里有 futFop,主要是为了期货和期货期权场景。
返回多行是不是接口重复了
Section titled “返回多行是不是接口重复了”不是。每一行通常对应一个交易所维度的参数集合,例如 SMART、CBOE、PHLX 等。实际使用时可以先看 SMART,再按需要检查具体交易所。
为什么请求成功后仍可能找不到具体期权
Section titled “为什么请求成功后仍可能找不到具体期权”因为这个接口返回的是到期日集合和行权价集合。官方说明过,某些到期日和行权价组合可能无法组成有效期权合约。构造具体期权后,应再用 reqContractDetails() 做一次确认。