OPTION CHAIN CONTRACT
Optopsy 数据契约:期权链要长什么样,怎么喂进去
Optopsy 的首要门槛不是算法,而是数据形状。它要的是期权链——每条记录对应「某标的、某报价日、某到期日、某行权价的一张期权」,最少 8 列,其中 delta 是硬性要求。
- 8 个必需列:
underlying_symbol、option_type、expiration、quote_date、strike、bid、ask、delta。 delta是硬要求:没有它,任何策略都无法选出行权价。- 按索引不按列名:
csv_data()的参数是从 0 开始的整数列号,赋错值不会报错,只会映射出错误的列。 - 8 个可选列:从希腊字母到成交量,只在特定功能下才需要。
csv_data() 用整数列号,不用列名options_data() 接已命名的 DataFrameoptopsy/datafeeds.py 的 csv_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+ 环境实跑。
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 不会替你发现。
预期输出:拿到一张「列名 → 列号」的对照表
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 全为负或异常 | 若 expiration 与 quote_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–1 | df['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_symbol、option_type、expiration、quote_date、strike、bid、ask、delta。其中 delta 是硬性要求——没有它任何策略都选不出行权价。以官方 getting-started 文档与源码的必需列常量为准。
csv_data() 到底是按列名还是按列号?
按列号(整数索引,从 0 开始)。这是它和 options_data() 关键区别:后者接收列名正确的 DataFrame,前者适配任意表头顺序。列号赋错不会报错,只会静默映射到错误的列。
我能直接用股票的日线数据吗?
不能直接当期权链用。Optopsy 的策略作用在期权链上,需要 strike、expiration、delta 这些字段。股票日线另有用途:作为技术指标信号的输入,以及 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 这类过滤尽早缩小候选池。