You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用blpapi或xbbg获取彭博SPX指数期权历史数据问题求解

如何通过Python从彭博终端获取SPX指数期权历史数据

我试图通过Python从彭博获取SPX INDEX期权数据,目标是获取指定日期下所有期权的现货价格、买价、卖价和隐含波动率。

Excel端已验证实现逻辑:先通过BQL语句筛选指定到期日的所有期权,代码为BQL("filter(options('SPX Index'),expire_dt=='"+"08/15/21"+"')","ID_SECURITY_DES,px_bid().value","showids=false","cols=2;rows=495"),可生成包含期权名称、价格、行权价、期权类型(看涨/看跌)的列,再通过BDH函数逐行查询每个期权的全量字段信息,示例代码为BDH('SPX US 08/15/21 4440P Index','Ivol_MID','08/18/21','08/18/21')。


原生blpapi实现报错问题

代码如下:

import blpapi
import pandas
import csv
options = blpapi.SessionOptions()
options.setServerHost('localhost')
options.setServerPort(8194)

SECURITY_DATA = blpapi.Name("securityData")
SECURITY = blpapi.Name("security")
FIELD_DATA = blpapi.Name("fieldData")
FIELD_ID = blpapi.Name("fieldId")
OPT_CHAIN = blpapi.Name("OPT_CHAIN")
SECURITY_DES = blpapi.Name("Security Description")


def getOptionChain (sec_list):
    import pdb;pdb.set_trace()
    session = blpapi.Session(options)
    session.start()
    session.openService('//blp/refdata')
    refDataService = session.getService("//blp/refdata")
    request = refDataService.createRequest("ReferenceDataRequest")
    for s in sec_list:
        request.append("securities",s)
    request.append("fields", "OPT_CHAIN")
    cid = session.sendRequest(request)
    try:
        while(True):
            ev = session.nextEvent(500)
            response = []
            for msg in ev:
                if cid in msg.correlationIds():
                    securityDataArray = msg.getElement(SECURITY_DATA)
                    for securityData in securityDataArray.values():
                        fieldData = securityData.getElement(FIELD_DATA)
                        for field in fieldData.elements():
                            for n in range(field.numValues()):
                                fld = field.getValueAsElement(n)
                                response.append (fld.getElement(SECURITY_DES).getValueAsString())
            if ev.eventType() == blpapi.Event.RESPONSE:
                break
    finally:
        session.stop()
    return response


def getPX (sec_list, fld_list):
    opt_chain_list = getOptionChain(sec_list)
    session = blpapi.Session(options)
    session.start()
    session.openService('//blp/refdata')
    refDataService = session.getService("//blp/refdata")
    request = refDataService.createRequest("ReferenceDataRequest")
    for s in opt_chain_list:
        request.append("securities",s)
    for f in fld_list:
        request.append("fields",f)
    cid = session.sendRequest(request)
    try:
        response = {}
        while(True):
            ev = session.nextEvent(500)
            for msg in ev:
                if cid in msg.correlationIds():
                    securityDataArray = msg.getElement(SECURITY_DATA)
                    for securityData in securityDataArray.values():
                        secName = securityData.getElementAsString(SECURITY)
                        fieldData = securityData.getElement(FIELD_DATA)
                        response[secName] = {}
                        for field in fieldData.elements():
                            response[secName][field.name()] = field.getValueAsFloat()
            if ev.eventType() == blpapi.Event.RESPONSE:
                break
    finally:
        session.stop()                   
    tempdict = {}
    for r in response:
        tempdict[r] = pandas.Series(response[r])
        data = pandas.DataFrame(tempdict)
    return data

sec = ["SPX INDEX Equity"]
fld = ["PX_ASK","PX_BID"]
print(getPX(sec,fld))

原始报错信息:

blpapi.exception.InvalidArgumentException: Choice sub-element not found for name 'securityData'. (0x00020002)

报错翻译:blpapi异常.无效参数异常:未找到名称为'securityData'的选择子元素 (0x00020002)


xbbg库实现问题

尝试使用xbbg查询的代码如下:

blp.bdh(tickers='SPX INDEX',flds=['PX_LAST','Bid','Ask'],start_date='09-26-20',end_date='09-30-20')

返回空DataFrame。


问题补充(2021年8月19日)

已修正标的证券名称,但问题仍未解决,使用sec = ["SPX US 08/15/21 4440P Index"]、fld = ["IVOL"]查询时仍返回上述相同报错。
当前已知直接指定具体期权标的可查询到目标数据,但需要先获取完整的期权列表和到期日列表,才能按指定到期日查询数据。


解决方案

问题根因

  1. 标的代码格式错误:彭博API要求的SPX标的正确格式为SPX Index,不需要额外加Equity后缀,错误的标的代码会导致API返回无有效securityData节点的响应,触发对应报错。
  2. OPT_CHAIN字段默认返回全量到期日期权,未按指定到期日过滤,查询量过大也可能导致响应异常。
  3. xbbg的bdh是历史时序数据查询接口,直接传入指数标的查期权相关字段自然返回空,需要先拿到期权链后再批量查询。

原生blpapi修正后实现

import blpapi
import pandas as pd

# 配置会话
session_opts = blpapi.SessionOptions()
session_opts.setServerHost('localhost')
session_opts.setServerPort(8194)
session = blpapi.Session(session_opts)
session.start()
session.openService('//blp/refdata')
ref_svc = session.getService('//blp/refdata')

def get_filtered_option_chain(underlying: str, expire_date: str) -> list:
    """
    获取指定标的、指定到期日的期权代码列表
    :param underlying: 标的代码,如'SPX Index'
    :param expire_date: 到期日,格式为'MM/DD/YY'
    """
    req = ref_svc.createRequest("ReferenceDataRequest")
    req.append("securities", underlying)
    req.append("fields", "OPT_CHAIN")
    # 新增到期日过滤参数,避免返回全量期权
    req.set("overrides", f"OPTS_CHAIN_EXP_DT={expire_date}")
    
    cid = session.sendRequest(req)
    opt_list = []
    while True:
        ev = session.nextEvent(1000)
        for msg in ev:
            if cid not in msg.correlationIds():
                continue
            if msg.hasElement("securityData"):
                sec_data = msg.getElement("securityData")
                for sec in sec_data.values():
                    field_data = sec.getElement("fieldData")
                    if field_data.hasElement("OPT_CHAIN"):
                        opt_chain = field_data.getElement("OPT_CHAIN")
                        for i in range(opt_chain.numValues()):
                            opt_item = opt_chain.getValueAsElement(i)
                            opt_code = opt_item.getElementAsString("Security Description")
                            opt_list.append(opt_code)
        if ev.eventType() == blpapi.Event.RESPONSE:
            break
    return opt_list

def get_option_fields(opt_list: list, fields: list, trade_date: str) -> pd.DataFrame:
    # 单日历史数据用BDH接口更稳定,和Excel端逻辑一致
    req = ref_svc.createRequest("HistoricalDataRequest")
    for opt in opt_list:
        req.append("securities", opt)
    for f in fields:
        req.append("fields", f)
    req.set("startDate", trade_date)
    req.set("endDate", trade_date)
    req.set("nonTradingDayFillOption", "ALL_CALENDAR_DAYS")
    
    cid = session.sendRequest(req)
    res = []
    while True:
        ev = session.nextEvent(1000)
        for msg in ev:
            if cid not in msg.correlationIds():
                continue
            if msg.hasElement("securityData"):
                sec_data = msg.getElement("securityData")
                sec_name = sec_data.getElementAsString("security")
                field_data = sec_data.getElement("fieldData")
                for i in range(field_data.numValues()):
                    item = field_data.getValueAsElement(i)
                    date = item.getElementAsString("date")
                    row = {"ticker": sec_name, "date": date}
                    for f in fields:
                        row[f] = item.getElementAsFloat(f) if item.hasElement(f) else None
                    res.append(row)
        if ev.eventType() == blpapi.Event.RESPONSE:
            break
    session.stop()
    return pd.DataFrame(res)

# 调用示例
if __name__ == "__main__":
    # 1. 获取2021年8月15日到期的SPX期权列表
    opt_codes = get_filtered_option_chain("SPX Index", "08/15/21")
    # 2. 查询2021年8月18日的买价、卖价、中期隐含波动率
    df = get_option_fields(opt_codes, ["PX_BID", "PX_ASK", "IVOL_MID"], "08/18/21")
    print(df.head())

xbbg简化实现

如果用xbbg可以省去自行解析响应的逻辑,代码如下:

from xbbg import blp

# 先获取指定到期日的期权链
opt_chain = blp.bdtick('SPX Index', flds=['opt_chain'], overrides={'OPTS_CHAIN_EXP_DT': '08/15/21'})
opt_codes = opt_chain['security_description'].tolist()

# 批量查历史数据
df = blp.bdh(
    tickers=opt_codes,
    flds=['px_bid', 'px_ask', 'ivol_mid'],
    start_date='2021-08-18',
    end_date='2021-08-18'
)

内容的提问来源于stack exchange,提问作者curious123456789

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.07 10:33:03