跳转到内容

请求期权链

reqSecDefOptParams() 用来请求某个标的的期权链参数。新手最容易卡住的地方是 underlyingConId,它不是股票代码,而是 IBKR 合约 ID。AAPL 股票常见返回的 conId265598,实际项目应以 reqContractDetails() 返回为准。

app.reqSecDefOptParams(
reqId,
underlyingSymbol,
futFopExchange,
underlyingSecType,
underlyingConId,
)
参数类型中文含义AAPL 示例说明
reqIdint请求编号9503自己分配,用来把请求和回调对应起来。
underlyingSymbolstr标的代码"AAPL"股票代码、期货根代码等。
futFopExchangestr期货或期货期权交易所""股票期权通常传空字符串;期货期权才常需要指定交易所。
underlyingSecTypestr标的证券类型"STK"股票是 STK,期货是 FUT
underlyingConIdint标的合约 ID265598建议先用 reqContractDetails() 查出准确值。

请求期权链前,至少要确认三件事:

检查项为什么重要
TWS 或 IB Gateway 已启用 APISocket 没开时 Python 无法连接。
标的 conId 已确认只靠代码可能遇到歧义合约。
已收到 nextValidId说明 API 握手完成,可以发送请求。

下面示例只输出数量和范围,避免把很长的到期日集合、行权价集合全部刷屏。

from __future__ import annotations
import threading
from collections import Counter
from ibapi.client import EClient
from 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=True
OPTION_END_RECEIVED=True
OPTION_ROW_COUNT=20
OPTION_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:1
NON_INFO_ERROR_COUNT=0

同一个代码在不同市场、不同产品类型下可能不是同一个合约。conId 能把标的唯一化,减少歧义。

美股股票期权通常传空字符串 ""。这个参数名称里有 futFop,主要是为了期货和期货期权场景。

不是。每一行通常对应一个交易所维度的参数集合,例如 SMARTCBOEPHLX 等。实际使用时可以先看 SMART,再按需要检查具体交易所。

为什么请求成功后仍可能找不到具体期权

Section titled “为什么请求成功后仍可能找不到具体期权”

因为这个接口返回的是到期日集合和行权价集合。官方说明过,某些到期日和行权价组合可能无法组成有效期权合约。构造具体期权后,应再用 reqContractDetails() 做一次确认。