跳转到内容

合约定义与发现

事件交易的第一步是合约发现。只要合约字段没有被 TWS 正确识别,后面的行情、下单、撤单都没有意义。

在 TWS 搜索事件合约
-> 打开合约详情
-> 记录合约 ID、本地代码、交易所、到期日、行权价、方向
-> 用 reqContractDetails() 在脚本里验证
-> 保存验证通过的字段

不要先从策略需求倒推合约字符串。事件合约更新快,直接从 TWS 可见合约出发更可靠。

from ibapi.contract import Contract
def request_contract_details(app, req_id):
contract = Contract()
contract.symbol = "GCE"
contract.secType = "OPT"
contract.exchange = "FORECASTX"
contract.currency = "USD"
contract.lastTradeDateOrContractMonth = "20251231"
contract.strike = 40500
contract.right = "C"
# 发送后等待 contractDetails() 和 contractDetailsEnd() 回调
app.reqContractDetails(req_id, contract)

如果返回 0 条并出现错误码 200,说明这组字段不能定位合约。此时应补充 localSymbol、到期日、行权价和方向,或者直接使用 TWS 合约详情里的 conId

示例连接中,GCE ForecastEx 合约详情请求成功:

DETAIL_ROWS_FORECAST_GCE_FULL=1
DETAIL=reqId=95191;conId=753279929;symbol=GCE;secType=OPT;exchange=FORECASTX;localSymbol=GCE_1225_40500_YES;tradingClass=GCE;lastTradeDateOrContractMonth=20251231;strike=40500.0;right=C;multiplier=1;longName=Global Carbon Dioxide Emissions

宽泛请求 GCE + OPT + FORECASTX + USD 会返回多条候选,其中也可能包含更远到期日的合约。程序应该按到期日、方向、行权价、localSymbolconId 继续筛选,不能只取第一条。

def contractDetails(self, reqId, contractDetails):
contract = contractDetails.contract
print("conId:", contract.conId)
print("symbol:", contract.symbol)
print("secType:", contract.secType)
print("exchange:", contract.exchange)
print("localSymbol:", contract.localSymbol)
print("tradingClass:", contract.tradingClass)
print("longName:", contractDetails.longName)
print("realExpirationDate:", contractDetails.realExpirationDate)

事件合约还可能返回 eventContract1eventContractDescription1eventContractDescription2 等字段。不同 API 版本和合约类型下字段完整度可能不同,所以代码要允许字段为空。

字段是否建议保存说明
conId建议最精确,适合继续做行情和订单验证。
localSymbol建议便于和 TWS 界面核对。
symbol建议业务层展示和搜索使用。
secType必须区分 OPTFOP 等类型。
exchange必须ForecastEx 和 CME 不能混用。
currency必须常见为 USD
lastTradeDateOrContractMonth视合约而定到期类合约通常需要。
strike / right视合约而定用来区分事件结果或方向。