OPTION CHAIN CONTRACT

Optopsy 数据契约:期权链要长什么样,怎么喂进去

Optopsy 的首要门槛不是算法,而是数据形状。它要的是期权链——每条记录对应「某标的、某报价日、某到期日、某行权价的一张期权」,最少 8 列,其中 delta 是硬性要求。

  • 8 个必需列underlying_symboloption_typeexpirationquote_datestrikebidaskdelta
  • delta 是硬要求:没有它,任何策略都无法选出行权价。
  • 按索引不按列名csv_data() 的参数是从 0 开始的整数列号,赋错值不会报错,只会映射出错误的列。
  • 8 个可选列:从希腊字母到成交量,只在特定功能下才需要。
必需 8 列标的 / 类型 / 到期日 / 报价日 / 行权价 / 买价 / 卖价 / Delta
可选 8 列标的价 / 收盘价 / gamma / theta / vega / 隐含波动率 / 成交量 / 持仓量
索引映射csv_data() 用整数列号,不用列名
加载出口options_data() 接已命名的 DataFrame
依据 optopsy/datafeeds.pycsv_data() 签名与必需列常量绘制的示意图;非官方图。

COLUMN CONTRACT

Optopsy 数据格式要求:8 个必需列 + 8 个可选列

下表按「缺了会怎样」组织。首列是列名,注意全部为小写下划线格式,且大小写敏感

列名必需?含义与取值缺失或错误的后果适用场景注意点
underlying_symbol必需标的代码,如 SPX / SPY / QQQ无法按标的去重与分组,结果错乱所有策略同一份数据可含多个标的
option_type必需期权类型,接受 'c'/'p''call'/'put'策略选不到对应类型的腿所有策略混用两种写法时先统一,避免大小写不一致
expiration必需到期日无法计算 DTE,DTE 过滤全失效所有策略quote_date 一起决定 DTE
quote_date必需报价日(每个交易日一行)无法拼出入场/出场两条报价所有策略期权链数据量大的主要原因就是它
strike必需行权价选不出合约所有策略数值型,不要带货币符号
bid必需买价无法按 min_bid_ask 过滤,也无法算成交价所有策略用于 mid / spread / liquidity 三种滑点模式
ask必需卖价同上所有策略与 bid 一起构成价差过滤的依据
delta必需期权 Delta(正数,0–1 量纲)任何策略都无法运行——这是最硬的一条所有策略引擎不自己算 Greek,完全依赖这一列
underlying_price可选标的价格部分信号无法使用需要标的价做条件的场景close 用途不同,按需提供
close可选标的收盘价部分信号与股票代理腿退化为近似信号计算、Covered 类策略信号主要在股票数据上算,这里更多是备用
gamma / theta / vega可选其余希腊字母当前策略逻辑不依赖它们你自己做扩展分析时有就带上,便于后续自建分析
implied_volatility可选隐含波动率IV Rank 类信号不可用要用 IV Rank 过滤入场时必需IV Rank 是仅有跑在期权链上的信号
volume可选成交量liquidity 滑点模式不可用想用成交量动态滑点时必需官方也接受用 open_interest 替代
open_interest可选持仓量同上同上volume 二选一即可

依据 optopsy/datafeeds.py_REQUIRED_COLUMNS 常量与官方 getting-started 的字段表整理。

一句话记忆:前面 8 列决定「能不能跑」,后面 8 列决定「能不能跑更高级的功能」。只有 delta 是可以让你连最简单的 long_calls 都跑不出来的那一列。

LOADING BY INDEX

Optopsy csv_data 用法说明:四步调用与整数索引这个坑

这是新手最容易出错的一处:csv_data() 的参数是列号,不是列名,赋错不会报错。

写法取自官方 getting-started 与 examples 文档;第 3 步的验证方法为本项目补充。本站未在 Python 3.12+ 环境实跑。

  • 先看你的 CSV 表头,数清每一列的位置
    head -1 options.csv
    # 输出示例:Symbol,Type,Expiration,QuoteDate,Strike,Bid,Ask,Delta
    # 列号:      0      1        2           3        4     5    6     7

    列号从 0 开始。上例中 Delta 在第 8 列,所以 delta=7。数错一位会把买价当成行权价,而 pandas 不会替你发现。

    预期输出:拿到一张「列名 → 列号」的对照表

  • 把列号填进 csv_data
    import optopsy as op
    
    data = op.csv_data(
        "options.csv",
        underlying_symbol=0,
        option_type=1,
        expiration=2,
        quote_date=3,
        strike=4,
        bid=5,
        ask=6,
        delta=7,
    )
    print(data.columns.tolist())

    未显式指定的可选列保持缺省。也可以传 start_date / end_date 只取一段区间,避免一次载入过大数据。

    预期输出:打印出标准化后的列名列表(underlying_symbol 等),顺序与你的 CSV 无关

  • 验证列有没有映射对(本站加的一步)
    print(data[["strike", "bid", "ask", "delta"]].describe())

    如果 delta 的均值在 0.3 附近、strike 是合理的价格量级,说明映射正确。如果 delta 出现了上千的值,多半是列号错位把别的列当成了 Delta。

    预期输出:delta 落在 0–1,strike 与标的价位量级一致

  • 跑一次最简策略确认链路通
    print(op.long_calls(data, max_entry_dte=45, exit_dte=0).head())

    先用最简策略验证;如果这一步有结果,再换成你想研究的多腿策略。

    预期输出:按 dte_range / delta_range 分组的统计表

  • 为什么用索引而不是列名

    期权链数据来源五花八门(CBOE 导出、券商导出、第三方 API),列名几乎没有统一的可能。用整数索引可以适配任意表头顺序,代价就是用户必须自己数列号。

    已经有 DataFrame 怎么办

    如果你的 DataFrame 列名已经符合标准(underlying_symbol 等),直接用 options_data(),它接收的是列名而不是索引,会做校验与规范化。这是两条并行的入口。

    DATES AND SILENT FAILURES

    Optopsy 日期与静默失败说明:三处不会报错的错误

    下面三件事 Optopsy 都不会告诉你「做错了」,只会让你得到偏少或错误的结果。

    静默失败发生了什么表现怎么预防
    日期格式混杂官方称能自动推断 ISO(2023-01-20)、美式(01/20/2023)、欧式(20/01/2023)三种格式同一文件里混用不同格式时,部分行解析失败或解析成错误日期统一成 ISO 格式再导入;导入后打印 quote_date 的最小值与上界值核对
    期权链与信号数据的时间戳不匹配期权链可能是日期(午夜)而信号数据来自行情(含时区或收盘时刻)静默的内连接失败:按日期合并时匹配不上,结果莫名变少甚至为空库内提供 normalize_dates() 在匹配边界统一归一;自己在合并前也应先对齐到日期粒度
    DTE 全为负或异常expirationquote_date 被解析反了,DTE 会变成负数DTE 过滤后没有任何候选,结果为空打印 (expiration - quote_date).dt.days.describe(),正常应为正数且量级在 0–1000 天
    option_type 大小写/写法不一致'C' / 'Call' / 'call' 混用部分合约被策略忽略,样本莫名偏少导入前统一为小写 'c' / 'p'

    日期推断与归一化依据官方 getting-started 与 optopsy/timestamps.py 的模块说明;其余两条为常见的静默数据问题。

    normalize_dates() 是库的公开导出之一,官方在源码里明确说明它的用途是「在匹配边界统一时间戳,避免时区与时刻差异造成静默的 join 失败」。这是官方文档正文里没有强调、但排查「数据明明有却匹配不上」时非常关键的一个函数。

    OTHER ENTRY POINTS

    Optopsy 除了 CSV 还有哪些加载方式:另外三条路径介绍

    已有 DataFrame:options_data()

    列名已经正确时用这个函数,它会做校验与规范化,并同样支持 start_date / end_date 截取。如果你的数据来自数据库或上游接口,这是最自然的一条路。

    读缓存:load_cached_options() / load_cached_stocks()

    读取由 optopsy-data download 生成的 Parquet 缓存并规范化为标准结构。需要 optopsy[data] 额外安装(依赖 pyarrow);只装核心库会报缺模块。

    加载方式输入何时用依赖注意点
    csv_data()CSV 文件 + 整数列号数据来自导出文件、列顺序非标准pandas列号赋错不报错,必须自己核对
    options_data()列名已正确的 DataFrame数据来自数据库或上游接口pandas列名要求大小写敏感且完全一致
    load_cached_options()标的代码(读 Parquet 缓存)已用内置 CLI 下载过数据optopsy[data](pyarrow)缓存默认在 ~/.optopsy/cache/,可用 OPTOPSY_DATA_DIR 改位置
    load_cached_stocks()标的代码(读股票缓存)要算技术指标信号时optopsy[data]信号跑在股票 OHLCV 上,这一条是信号的输入源
    自己构造 DataFrame内存里的 DataFrame数据来自你自己的数据管道pandas必须包含 8 个必需列且 delta 有效
    先用内置 CLI 再读缓存optopsy-data download SPY → 读缓存想省去找数据的功夫EODHD 的 API Key属第三方付费服务;下载只补缺口,历史数据不设过期

    依据 optopsy/datafeeds.py 的四个公开函数与官方 data 文档整理。

    国内起步最省事的一条是最后一列之外的「自己构造 DataFrame」:不依赖任何境外服务,只要 8 列齐备且 Delta 有效就能开跑。

    DATA QUALITY CHECKS

    Optopsy 上手前怎么做数据体检:六项检查方法

    期权链数据的问题绝大多数在跑策略之前就能发现。下面六项按性价比排序,都能用几行 pandas 完成。

    体检项怎么查健康标准不健康时的处置
    必需列是否齐备对照 8 个必需列名逐一点名8 列全部存在且类型正确delta 时优先补数据源,而不是想办法绕过
    Delta 是否有值且在 0–1df['delta'].describe()无缺失、取值范围落在 0 到 1 之间百分数表示时先除以 100;缺失行删除或剔除该报价日
    DTE 分布是否合理(df.expiration - df.quote_date).dt.days.describe()全部为正,量级 0–1000 天为负说明日期解析反了;量级异常说明格式化错误
    每个报价日的合约数df.groupby(['quote_date','expiration']).size().describe()每个(报价日 × 到期日)至少有足够多的行权价覆盖 Delta 区间合约太少的日期会让多腿策略选不出组合
    bid/ask 是否可用df[(df.ask >= df.bid)].shape绝大多数行满足 ask ≥ bid 且两者都 > 0倒挂或为 0 的报价会导致滑点计算异常,建议先剔除
    Delta 的分布是否覆盖你的目标区间df['delta'].abs().describe()你要用的目标 Delta(如 0.20)在分布内有足够密的采样点分布偏一侧时,调 target 而不是硬撑窄区间

    依据官方 getting-started 的空结果排查清单与标准期权链数据常识整理;具体阈值应结合你自己的标的与数据源调整。

    FAQ

    Optopsy 数据格式常见问题

    Optopsy 最少需要哪些列?

    8 个必需列:underlying_symboloption_typeexpirationquote_datestrikebidaskdelta。其中 delta 是硬性要求——没有它任何策略都选不出行权价。以官方 getting-started 文档与源码的必需列常量为准。

    csv_data() 到底是按列名还是按列号?

    列号(整数索引,从 0 开始)。这是它和 options_data() 关键区别:后者接收列名正确的 DataFrame,前者适配任意表头顺序。列号赋错不会报错,只会静默映射到错误的列。

    我能直接用股票的日线数据吗?

    不能直接当期权链用。Optopsy 的策略作用在期权链上,需要 strikeexpirationdelta 这些字段。股票日线另有用途:作为技术指标信号的输入,以及 Covered 类策略的股票腿数据。

    没有 volume 列会有影响吗?

    只影响一件事:slippage='liquidity' 这种按成交量动态计算的滑点模式不可用。用默认的 'mid''spread' 不受影响;也可以用 open_interest 列替代。

    为什么我的数据明明有内容,跑出来却是空的?

    按这个顺序排查:①有没有 delta 列且取值在区间内;②leg*_delta 区间是不是比数据实际分布更窄;③min_bid_ask 是否把候选全过滤掉;④DTE 条件是否超出数据覆盖范围。这四项也是官方文档给出的排查方向,详见逐腿 Delta页的排查表。

    从哪里能拿到期权链数据?

    官方文档点名了 EODHD(内置对接,需 Key)、CBOE DataShop、HistoricalOptionData.com、Polygon.io,以及券商导出(Schwab / IBKR / Tastytrade 等)。官方没有提供中国 A 股期权数据源,但引擎不限定市场,只要字段满足契约就行。以官方文档为准。

    数据量很大,加载会不会很慢?

    期权链天然是大数据——每个报价日、每个到期日、每个行权价都是一行。实用的做法是:先用 start_date / end_date 限定区间,再按需要过滤标的与到期日范围。官方也建议用 min_bid_ask 这类过滤尽早缩小候选池。

    下一步:怎么把整理好的数据跑成一条权益曲线

    模拟与绩效页给出 simulate() 的完整参数、selector 的四种选择方式、仓位限制与 ruin 截断,以及 12 个风险指标的口径与空数据返回值。