STRIKE SELECTION

逐腿 Delta 选股:Optopsy 的行权价是怎么定下来的

Optopsy 不让你手工指定行权价,而是给每条腿一个 Delta 目标区间,由引擎在你的期权链里挑出最接近目标的那一张合约。理解这条链路,才能解释为什么有时候一行结果都出不来。

  • 三个字段target 是理想值,min / max 是可接受区间,约束 min ≤ target ≤ max
  • Delta 取绝对值:区间用 0–1 的正数表示,与看涨看跌无关。
  • 数据里的 delta 列是硬要求:缺它,任何策略都无法选出行权价。
  • 每条腿独立选:多腿策略是先各自选好、再按日期与到期日拼组合。
按 DTE 过滤先由 max_entry_dte / exit_dte 框住候选合约
按买价价差过滤min_bid_ask 剔掉流动性差的合约
Delta 落在区间内保留 abs(delta)[min, max] 的候选
分组内取最近每组(标的 + 报价日 + 到期日 + 期权类型)取最接近 target 的一行
依据 optopsy/filters.py 的过滤与选取函数绘制的示意图;非官方流程图。

TARGETRANGE

Optopsy TargetRange 三个字段与默认值:怎么给每条腿设 Delta

每个腿的 Delta 参数接受两种写法:一个 TargetRange 对象,或者等价的字典。

字段 / 角色含义典型取值适用场景注意点
target理想 Delta 值,引擎挑最接近它的合约0.30决定行权价离平值有多远它是「最近」而不是「精确等于」,实际选中的合约不一定正好等于 target
min可接受的最小 Delta,低于它的合约被剔除0.20防止选到过远的虚值区间设太窄会导致选不出合约、结果为 0 行
max可接受的 Delta 上界,高于它的合约被剔除0.40防止选到过近的实值min 一起决定候选池大小
标准虚值腿(默认)离平值较远、时间价值为主0.30 / 0.20 / 0.40大多数方向性策略的主腿官方称其为 standard OTM leg 的默认值
平值腿(默认)贴近平值,时间价值占比偏高0.50 / 0.40 / 0.60跨式、宽跨的对称腿,或蝶式中间腿平值附近合约最密集,通常候选最多
虚值翼(默认)更远的保护腿0.10 / 0.05 / 0.20铁鹰、鹰式的外侧翼腿要与短腿的 Delta 明显分层,否则不再是翼
股票代理(默认)用深度实值 call 代理股票头寸0.80 / 0.60 / 0.95Covered / 领口系列不传 stock_data这是近似:真实股票的股息与流动性特征代理不出来
字典写法与 TargetRange 等价的普通字典{"target":0.30,"min":0.20,"max":0.40}不想额外 import 类型时键名必须准确,写错键会因严格校验而报错

依据官方参数文档(TargetRange 与默认 Delta 表)与 optopsy/types.py 的类型定义整理。

约束 min ≤ target ≤ max 由 Pydantic 在运行前校验,违反会直接报错而不是静默取默认值。这是官方标注的「严格参数校验」行为。

SELECTION PIPELINE

Optopsy 怎么从期权链选出一张合约:四步过滤步骤

三个过滤条件不是并行的,而是依次收窄候选池;最后一步的「取最近」是决定性的。

第 4 步依据 optopsy/filters.py_select_closest_delta 的实现(含其 docstring 对分组键的描述);前三步依据官方参数文档。

  • 先按到期日收窄:DTE 过滤
    results = op.long_calls(data, max_entry_dte=60, exit_dte=30)

    max_entry_dte 限制入场时的剩余到期天数,exit_dte 决定在哪一天出场(0 表示持有到期)。只有这两个参数都落在你的数据覆盖范围内,才能匹配到入场与出场两条报价。

    预期输出:候选集缩小到满足 DTE 条件的合约;不满足则整条链路无候选

  • 再按流动性收窄:bid-ask 过滤
    results = op.long_calls(data, max_entry_dte=45, min_bid_ask=0.10)

    min_bid_ask 剔除买卖价差低于阈值的合约。默认 0.05;流动性好的标的可以提到 0.15 甚至 0.20。这一步是「结果为空」的常见元凶之一。

    预期输出:短腿与翼腿都要能找到满足价差的合约,四腿策略尤其明显

  • 按 Delta 区间保留候选
    leg2_delta={"target": 0.20, "min": 0.15, "max": 0.25}

    只保留 abs(delta) 落在 [min, max] 内的合约。这一步直接把「目标区间太窄」变成「没有候选」。

    预期输出:每个腿各自得到一个候选池,彼此独立

  • 分组内取最接近 target 的一张
    # 源码实现要点(filters._select_closest_delta):
    # 1) 先按 [delta_min, delta_max] 过滤 abs(delta)
    # 2) 再按 (underlying_symbol, quote_date, expiration, option_type) 分组
    # 3) 每组内取 abs(delta) 最接近 target 的那一行

    分组键里的四个字段很关键:同一个报价日、同一个到期日、同一种期权类型才在同组竞争。所以同一个报价日不同到期日会各自选出一张,这正是「按 DTE 分桶」结果能出现多行的原因。

    预期输出:每组恰好保留一行;多腿策略随后按日期与到期日把各腿拼起来

  • 为什么是「分组内取最近」而不是「全局排序」

    如果全局只留一行,那四腿策略就没法在同一个报价日取出四条不同的合约。按分组键取最近,等于在「每个报价日 × 每个到期日 × 每种期权类型」的口袋里各挑一张,多腿组合才有原料可用。

    Delta 是数据给的,不是算出来的

    Optopsy 直接消费你数据里的 delta 列,不自己计算希腊字母。所以不同数据源的 Delta 口径差异会直接反映到选股结果上——这是跨数据源比较结果时必须先对齐的东西。

    BEYOND THE DOCS

    Optopsy 官方文档没写的三处实现细节说明

    官方文档讲清了参数语义,但下面三点只在源码里能看到,而它们直接决定你会拿到哪一张合约。

    细节官方文档怎么写的源码里实际是对你的影响
    Delta 的比较方式写「Select strikes by delta」,未说明方向处理比较的是 abs(delta),区间也用 0–1 正数看跌腿不需要写负 Delta;写负数会导致区间过滤全空
    「最近」的分组边界只写 per-leg delta targeting分组键是标的 + 报价日 + 到期日 + 期权类型四个字段同一报价日的不同到期日会各自产生一行,直接影响你能看到几个 DTE 分桶
    不在区间的处理min/max 会排除超出的期权是「先过滤、再取最近」,不是在全部候选中排序后截取区间设得比数据实际 Delta 分布更窄时,该腿候选直接归零,而不是退化成取边界值

    对照官方参数文档与 optopsy/filters.py_select_closest_delta 实现逐条核对(采集 2026-09-23)。

    这三条不是官方文档的错误,而是抽象层级不同:文档面向使用者讲参数,源码面向实现讲分组。它们的差别恰恰是排查「为什么结果和预期不一样」时最有用的信息。

    LEG MAPPING

    Optopsy 腿数与参数的对应关系:怎么判断设错哪一腿

    多腿策略里,把 Delta 设到不存在的腿上不会报错,只会静默不生效。下表是完整映射。

    策略有效参数通常不设或用默认的数据要求注意点
    long_calls / short_puts 等单腿leg1_delta标准 8 列(含 delta只设 leg1 即可
    long_straddles / long_stranglesleg1_deltaleg2_delta标准 8 列跨式两腿 Delta 绝对值接近,宽跨需分层
    long_call_spread / short_put_spreadleg1_deltaleg2_delta标准 8 列两腿 Delta 太接近会选到相邻行权价,价差过窄
    call_back_spread / put_front_spread 等比例价差leg1_deltaleg2_delta标准 8 列腿的数量比是 1:2,参数仍是两个腿
    long_call_butterfly 等蝶式leg1_deltaleg2_deltaleg3_delta标准 8 列三腿行权价需单调,Delta 设反会选不出组合
    iron_condor 等四腿leg1_deltaleg4_deltaleg4_delta 有时用默认翼腿值标准 8 列四腿都要有满足价差的合约,这是最容易结果稀疏的一类
    covered_call / collarleg1_delta(股票代理)、leg2_delta(期权)可选 stock_data(真实 OHLCV)股票代理腿用深度实值 Delta 近似
    long_call_calendar 等日历 / 对角leg1_deltaleg2_delta + 两组 *_dte_*两个到期日都要有数据跨字段校验:前月 max DTE 必须小于后月 min DTE

    依据官方参数文档的腿数说明与各策略模块默认值整理。

    一个实操建议:先用 raw=True 打印出实际的 strikedelta_entry 列,看看引擎到底选中了哪几张合约。这比反复调参数更快定位问题。

    EMPTY RESULTS

    Optopsy 结果为空怎么办:按顺序排查的方法

    空结果是期权回测最常见的「报错」——它不报错,只是没有行。下面按命中概率从高到低排序。

    排查顺序检查什么怎么验证修法
    1数据里有没有 delta 列,值是否有效打印 df['delta'].describe() 看是否有值、范围是否在 0–1补上 Delta 列;若数据源用百分数表示(如 30),需先换算
    2leg*_delta 区间是否比数据实际分布更窄把区间临时放宽到 0.05–0.95 再跑一次若放宽后出结果,说明区间过窄;按数据实际分布重新设
    3min_bid_ask 是否把候选全过滤掉了min_bid_ask 降到 0.0 或 0.01 再跑流动性差的标的或长尾行权价需要更低阈值
    4DTE 条件是否超出数据覆盖范围打印 expiration - quote_date 的天数分布max_entry_dte / exit_dte 调到数据实际存在的范围内
    5数据的时间区间是否覆盖了你要测的日期检查 quote_date 的最小值与上界值扩大数据下载区间,或用 csv_data(start_date=..., end_date=...) 明确范围
    6多腿策略是否有一腿找不到合约逐腿先单独跑一次单腿策略,确认每条腿都有候选放宽最难满足的那一腿(通常是翼腿)的 Delta 区间
    7装的是不是老包检查 len(op.__all__)hasattr(op,'TargetRange')换到 Python 3.12–3.13 重装(见安装页)

    前四项来自官方 getting-started 的空结果排查清单,后三项由本项目的源码核验与 pip 版本实测补充。

    FAQ

    Optopsy 逐腿 Delta 常见问题

    TargetRange 里的 Delta 要写正数还是负数?

    正数。源码比较的是 abs(delta),区间也用 0–1 的正数表示,与看涨看跌方向无关。写负数会导致区间过滤后没有候选。以源码实现为准。

    设了 leg3_delta 但只跑两腿策略,会报错吗?

    不会报错,也不会生效。多余参数在严格校验通过后被忽略。这是很容易被忽略的静默行为,建议只设该策略真正用到的腿。

    为什么同一份数据,不同人跑出来的行权价不一样?

    因为 Optopsy 直接消费数据里的 delta 列,不自己计算希腊字母。不同数据源的 Delta 口径(模型、无风险利率假设、是否含股息)不同,选出的合约就会不同。跨数据源比较前要先对齐这一点。

    targetmin/max 三者是什么关系?

    target 是理想值,引擎选最接近它的合约;min/max 是硬性可接受区间,超出范围的合约先被剔除。约束是 min ≤ target ≤ max,由 Pydantic 在运行前校验,违反会直接报错。

    为什么四腿策略的结果明显比单腿少?

    每一腿都要在同一个报价日、同一个到期日找到满足 Delta 区间与 bid-ask 阈值的合约,四个条件同时成立的概率远低于单腿。这是结构性的,不是 bug。放宽翼腿 Delta 区间与 min_bid_ask 是最有效的两步。

    行权价选得太近或太远怎么办?

    target。把 target 调小会让行权价更远(更虚值),调大则更近平值。改 target 的同时记得同步调整 min/max,否则会出现「目标值不在可接受区间内」的校验错误。以官方参数文档为准。

    下一步:怎么把行权价决策接上真实的成交假设

    模拟与绩效页说明 simulate() 的参数、selector 四种选择策略、仓位限制与权益曲线,以及 12 个风险指标的口径。