Skip to content

Latest commit

 

History

History
967 lines (742 loc) · 65 KB

File metadata and controls

967 lines (742 loc) · 65 KB

← 返回项目 README

DeltaLab 使用文档

本文档面向首次使用 DeltaLab 的期权投资者,用户需要对期权 Delta 动态对冲有基本的了解,通过本文档可以快速上手进行各种类型期权的动态对冲回测。


1. 环境与依赖

1.1 Python 版本

pricing/Option_AS.py 使用了 match/case 语法,需要 Python 3.10+。建议 3.11 或更高版本。

1.2 必装第三方库

用途
numpy 全部数值计算
scipy Option_Vanillascipy.stats.norm;GUI 波动率分布图用 scipy.stats.norm
pandas 结果表格、Wind/CSV 数据读取
matplotlib 图表展示(GUI 在 gui_app.py 中强制使用 TkAgg 后端)
Pillow (PIL) 仅用于 tools/make_icon.py / make_banner.py / make_workflow.py 生成图标与 README 资源;运行 GUI 本身不需要
tkinter GUI 框架,标准库自带;个别 Linux 发行版需额外安装 python3-tk

1.3 可选依赖

触发条件 不装时的影响
WindPy 在「数据来源」选 Wind 选 Wind 模式运行时会抛 ImportError(提示见 pricing/wind_data.py_ensure_wind),需安装 Wind 终端 + Python 插件,并已登录

CSV 与模拟模式无 Wind 依赖,可在普通环境下完整使用。

让 PyInstaller 发布包支持 Wind

默认 CI 产出的 zip 不带 WindPy(GitHub runners 上没有 Wind 终端)。若要让发布包支持 Wind 数据源,需要在装有 Wind 终端且能 python -c "import WindPy" 的机器上本地打包

# 确认 WindPy 可用
python -c "import WindPy; print(WindPy.__file__)"

# 本地打包
pyinstaller --noconfirm deltalab.spec

deltalab.spec 的 WindPy 段会自动探测:

  • import 成功 → 打印 [deltalab.spec] WindPy 已安装, 打入发布包,并通过 collect_submodules / collect_dynamic_libs / collect_data_files 把 WindPy 一并收入 dist/DeltaLab/
  • import 失败 → 打印跳过原因,发布包仍可产出,只是 Wind 模式运行时会在 _ensure_wind() 里报错

冻结构建运行时,_ensure_wind() 会把原始 ImportError / DLL 加载错误拼进异常文本,并在 sys.frozen=True 时追加一句"请在装有 Wind 终端的机器上重新打包"的提示,方便定位到底是 DLL 丢了还是终端没登录。

1.4 字体

GUI 在启动时会按平台自动挑选可用 CJK 字体(见 gui_app.py 顶部字体配置):

  • Windows:Microsoft YaHei / SimHei / SimSun
  • macOS:PingFang SC
  • Linux:Noto Sans CJK SC

若系统找不到任何 CJK 字体,matplotlib 会回退到 DejaVu Sans,中文会显示为方块;属于显示问题,不影响数值结果。

1.5 窗口图标与资源文件

GUI 启动时会通过 BacktestApp._apply_window_icon() 自动加载 assets/ 下的图标:

  • Windows 优先走 assets/deltalab.ico(16/32/48/64/128/256 多尺寸),在任务栏/标题栏表现最佳。
  • macOS / Linux 或 Windows 上 .ico 加载失败时,回退到 assets/deltalab.png + iconphoto()
  • 两个文件都缺失时静默跳过,不影响主流程。

图标、banner、工作流示意图都是用 tools/ 下的脚本生成的(依赖 Pillow),可随时重跑调整外观:

python tools/make_icon.py      # 生成 assets/deltalab.png / .ico
python tools/make_banner.py    # 生成 assets/banner.png (README 顶部)
python tools/make_workflow.py  # 生成 assets/workflow.png (工作流示意图)

2. 启动方式

在仓库根目录执行:

python gui_app.py

入口为 gui_app.pymain()。窗口默认大小 1600x1000,最小尺寸 1200x720

Windows 注意事项

  • 路径中含中文一般没问题,但若遇到 Wind/CSV 加载失败,可优先排查路径。
  • 高 DPI 屏幕下,左侧参数面板已包了 Canvas + Scrollbar,可滚轮浏览,不会出现底部按钮被裁的情况。

3. 界面总览

主窗口由 _build_ui() 构建并分为左右两栏(ttk.PanedWindow),可拖动中间分隔条。

3.1 左侧:参数与运行区

由上至下分四块:

  1. 期权类型 —— 选大类与子类型
  2. 期权参数 —— 随期权大类动态生成的输入框
  3. 回测设置 —— 数据来源、对冲策略、滑点、合约乘数等
  4. 运行按钮区 —— 运行回测 / 保留当前结果到对比 / 回测结果对比 / 绘制结构图 / 进度条

3.2 右侧:结果标签页(ttk.Notebook

Tab 内容
回测摘要 单路径盈亏分解 + Greeks 统计 + 蒙特卡洛分布统计(若有)
回测结果对比 只管理会话内已保留的普通回测快照,并支持实时勾选对比
◷ 历史择优 独立配置历史候选策略与参数,展示 CSV / Wind 滚动择优结果,并将推荐衔接到单次回测
对冲图表 6 宫格:标的路径、Delta 与持仓、Gamma、Vega、Theta、累计盈亏
波动率分析 4 宫格:滚动 RV vs IV、累计 RV、价差、日收益分布
盈亏分布 仅模拟模式且 n_paths>1 时显示蒙特卡洛分布
结构分析 绘制结构图 后展示当前期权结构和价格 / Greeks 对 S 的扫描曲线
每日明细 只显示建仓、策略、收盘兜底与终止对冲触发记录的表格 + 导出 CSV

底部状态栏实时显示「就绪 / 正在运行 / 完成」。


4. 参数详解

下文中所有参数名、控件值、字符串均直接对应 gui_app.py 当前代码,引用时用行内 code 格式。

4.1 期权类型

控件 选项 默认 说明
大类 香草期权 (Vanilla) / 累计期权 (Decumulator) / 亚式期权 (Asian) / 气囊期权 (Airbag) / 雪球期权 (Snowball) 香草期权 (Vanilla) OPTION_CLASSES
子类型 跟随大类切换 各大类的第一个 子类型驱动 optiontype 字段,决定走哪一种 payoff

各大类的子类型集(见 gui_app.py:OPTION_CLASSES):

  • VanillaEu
  • DecumulatorOpt_Decumulator_Back / Opt_Decumulator_Fix / Opt_EnDecumulator / Opt_EnDecumulator_Fix / Opt_ASGQ_call_put / Opt_ASGQ_EP / Opt_ASGQ_EF / Opt_ASGQ_DP / Opt_ASGQ_DF
  • AsianAsian / EnhanceAsian
  • AirbagOpt_Airbag
  • SnowballOpt_Snowball

每个子类型的 payoff 公式见 gui_app.py:STRUCTURE_DOCS,也会在「结构分析」Tab 顶部展示。

4.2 期权参数(按大类分组)

每个字段默认渲染为一个 Entry,初始值取自 OPTION_CLASSES[...]["params"] 的 default;空字符串会被解释为 0(见 _collect_gui_state)。若参数元组带有第 5 项 choices 映射(例如 cp),则改渲染为只读 Combobox,_collect_gui_state 会按映射将显示文本还原为内部数值。

香草期权 (Vanilla)

参数 含义 类型 默认
s0 初始价格 S0 float 100.0
K 行权价 float 100.0
T_days 期限(交易日) int 22
sigma 波动率(年化,小数) float 0.18
cp 方向,下拉选择 看涨 (Call) / 看跌 (Put),内部映射为 1 / -1 int 1(看涨)
r 无风险利率 float 0.03
q 分红率 float 0.03

定价走 BS 封闭解(Option_Vanilla.blsprice)。

累计期权 (Decumulator)

参数 含义 类型 默认
s0 初始价格 float 100.0
K 行权价 float 90.0
T_days 剩余期限(交易日) int 20
T_over 已过天数 int 0
sigma 波动率 float 0.18
H 障碍价格(敲出/熔断) float 110.0
N 杠杆倍数(跌破 K 后) int 2
cp 方向,下拉选择 看涨 (Call) / 看跌 (Put),内部映射为 1 / -1 int 1(看涨)
fix 固定赔付(*_Fix 子类型用) float 0.0,传 0 → 视为未设置(None)
P 保障价格(ASGQ_*P 用) float 0.0,传 0 → None
amount 固定金额(ASGQ_*F 用) float 0.0,传 0 → None
r / q 利率 / 分红 float 0.03 / 0.03
nPath MC 路径数 int 100000

注意 fix / P / amount 的「0 当 None」是在 OPTION_CLASSES["累计期权"]["build"] 里做的,对应不需要这些字段的子类型可以保留 0。

亚式期权 (Asian)

参数 含义 类型 默认
s0 初始价格 float 100.0
K 行权价 float 100.0
E 增强价(仅 EnhanceAsian 用) float 100.0
T 期限(交易日) int 22
N 观察日数(取末 N 日均价) int 22
sigma 波动率 float 0.15
cp 方向,下拉选择 看涨 (Call) / 看跌 (Put),内部映射为 1 / -1 int 1(看涨)
minPay 最低赔付 float 0.0
maxPay 最高赔付 float 999999.0(实际充当 +∞
r / q 利率 / 分红 float 0.03 / 0.03
nPath MC 路径数 int 100000

气囊期权 (Airbag)

参数 含义 类型 默认
s0 初始价格 float 100.0
K 行权价 float 100.0
KI 敲入价 float 90.0
T_days 期限(交易日) int 20
sigma 波动率 float 0.18
pr 未敲入参与率 float 0.8
pr_ki 已敲入参与率 float 1.0
cp 方向,下拉选择 看涨 (Call) / 看跌 (Put),内部映射为 1 / -1 int 1(看涨)
r / q 利率 / 分红 float 0.03 / 0.03
nPath MC 路径数 int 100000

观察日默认取 range(1, T_days+1)(每日观察),见 OPTION_CLASSES["气囊期权"]["build"]

雪球期权 (Snowball)

参数 含义 类型 默认
s00 入场价 / 期初名义价格,票息与亏损封顶基准 float 100.0
s0 当前价格 / 回测参考价 float 100.0
K 敲入后到期亏损参考价,通常等于 s00 float 100.0
KI 敲入价,雪球向下敲入,反雪球向上敲入 float 80.0
KO 期初敲出价,配合 ko_step 可形成降敲序列 float 103.0
T 剩余期限(交易日) int 243
first_obs_d 首次敲出观察日,支持预设或手填交易日数 int 63
obs_period_d 敲出观察间隔,支持预设或手填交易日数 int 21
ko_step 每个观察期 KO 递减点数,0 表示平敲 float 0.0
sigma 波动率 float 0.15
coupon 未敲入未敲出到期票息率(年化) float 0.15
coupon_ko 敲出票息率(年化) float 0.15
margin_call 保证金模式,下拉选择 追保(亏损不封顶) / 不追保(有限亏损) int 1(追保)
margin 不追保模式下最大亏损封顶比例,亏损封顶为 margin × s00 float 0.2
cp 方向,下拉选择 雪球 (卖看跌) / 反雪球 (卖看涨),内部映射为 -1 / 1 int -1
r / q 利率 / 分红 float 0.03 / 0.03
nPath MC 路径数,需为正偶数 int 20000

GUI 当前使用交易日计息 (act=1);敲入逐日观察,敲出按 first_obs_d + obs_period_d 生成观察日,并强制最后一次观察落在到期日。回测路径已知时,若观察日触发 KO,HedgeBacktest 会在敲出日提前截断并以敲出票息结算。

4.3 回测设置 — 数据来源

数据来源 是单选 Radiobutton,三选一:

标签 默认
simulate 模拟
csv CSV
wind Wind

切换数据来源时,_toggle_source 会动态显示对应参数子区。bar 粒度按数据源管理: 模拟来源显示可选的「模拟采样 bar/日」;Wind 显示「行情采样粒度」,用于决定向 Wind 请求何种行情;CSV / Wind 都不提供每日 bar 数输入框,真实行情由底层按 返回时间索引和交易 session 自动推导(最终实际为日频时固定为 1)。

模拟(simulate)

字段 含义 默认
种子 numpy default_rng 的基础种子 42
已实现波动率 用于生成路径的真实 σ;空 = 用期权 sigma(隐含) ""(空)
模拟路径数 MC 路径数。>1 时启用蒙特卡洛多路径分析;其中第一条用作主单路径展示 10
模拟采样 bar/日 每个模拟交易日生成的价格采样点数;控制路径离散化和固定间隔策略的触发检查粒度 1

调高 模拟路径数 会显著放慢运行(路径数 × 单路径回测耗时),但能稳定盈亏分布、对冲误差等统计量。

Per-path MC 种子:多路径模式下每条路径都有独立的 option.mc_seed = base_seed + path_idxhedge_backtest.py:277-282),避免所有路径共用同一批 MC 采样而人为压窄分布。整体仍可复现(固定 base_seed 即可),但子样本之间相互独立。

CSV

字段 含义 默认
文件 CSV 路径,旁边有 浏览… 按钮
价格列 CSV 中收盘价列名 close

底层走 HedgeBacktest.from_csv。日期列默认取第一列 (pd.read_csv(parse_dates=[0]))。仅日期索引按日频处理;带完整时间戳的日内 CSV 会按真实交易日组自动推导每日 bar 数,GUI 不提供手工覆盖。

Wind

字段 含义 默认
代码 Wind 标的代码 510050.SH
建仓日 期权建仓日(含)。勾选倒推时置灰只读,直接显示倒推结果 由截止日倒推
按数据截止日倒推 按期权剩余期限沿交易日历从截止日往前定位建仓日 开启
数据截止日 日期主控,也是自动倒推口径下的到期日(含) 最近一个已收盘交易日
行情采样粒度 只读:披露本次按策略推导出的 Wind 日频 / 分钟 bar 按策略自动

底层走 HedgeBacktest.from_wind。股票 / ETF 等普通单序列使用 "F"(前复权); 明确的具体期货合约使用不复权行情,避免对单一交割合约施加连续合约复权。Wind 日频的每日 bar 数为 1;分钟行情会结合实际返回的时间索引和品种交易 session 自动推导。

日期口径与历史择优同向:截止日往前推,而不是建仓日往后补。倒推走 pricing.trade_calendar(本地 data/tradingday.csv 离线优先),因此两端都落在 真实交易日上;日历没覆盖到截止日时直接报错,而不是拿旧日历末尾当锚点。默认截止 日不含当天:盘中拉行情会让最后一个交易日组只有半天 bar,分钟粒度的策略会被判成 盘中残段而拒绝运行。

两个日期框就是本次真实取数区间,界面不另设「实际区间」说明,因此两格都必须是 实际口径:倒推结果实时回填建仓日框(改截止日或期权期限都会跟着变),截止日填成 周末 / 节假日时在编辑完成(失焦或回车)后落到倒推真正使用的交易日锚点,即不晚于 它的最近交易日。逐次击键不落值,避免打断输入。

取消勾选后建仓日恢复可编辑(初值就是上次倒推出来的那天),截止日退化成请求上限 且不再被落到锚点;期限仍由回测引擎按 T 严格裁剪,若区间不足以覆盖期权剩余 期限,运行前或引擎仍会明确报错,不会压缩期限迁就数据。

「自动(推荐)」会根据当前单次回测策略选择请求口径:

当前策略 自动粒度原则
每日收盘 日频即可表达策略触发,优先请求日频
每日固定时刻 先按 Wind 代码交易时段跳过休市目标,再请求能覆盖全部生效 HH:MM 的兼容分钟行情
固定间隔 使用推荐的日内粒度,使盘中越带有机会被观察到

界面会披露 Wind 最终实际采用的采样粒度,便于解释固定间隔的触发次数与成本。 粒度不再提供手工覆盖:手动调粗只会静默漏掉 bar 内触发、让结论偏乐观。旧默认值中 “Wind 日频 + 固定时刻策略”需要运行后才报错的冲突,已由策略感知的自动粒度 消除。这里的「行情采样粒度」是请求和触发检查口径,并不是可编辑的每日 bar 数。

真实行情模式下,期权参数中的 s0 视为「参考价 S_ref」;GUI/底层会按 ratio = 真实首日价 / S_ref 自动缩放 K / KI / H / P / fix / amount 等价格量纲字段;雪球还会缩放 s00 / K / KI / KO,保证结构相对位置不被破坏。详细缩放表会展示在「回测摘要」Tab 中(由 _show_summary 渲染),逻辑在 hedge_backtest.py:_rescale_option_to_real_s0

4.4 回测设置 — 对冲参数

字段 含义 单位 / 取值 默认
交易成本率(%) 单边费率,按成交额收取;GUI 输入百分比,内部除以 100 float,单位 % 0.01(即 0.0001)
头寸方向 所有 GUI 回测唯一的方向来源;卖出 (Sell) / 买入 (Buy) 内部分别映射为 1 / -1 Radiobutton 卖出 (Sell)1
交易数量 quantity,将 Δ 转换为标的份数;必须为有限正数,不能用于翻转方向 float > 0 100
合约乘数 multiplier,每手对应的标的数量;0 = 不取整 float ≥ 0 5

GUI 默认值 5 与底层 HedgeBacktest.__init__(multiplier=5) 一致,语义是「每 5 个标的为一手,按手数取整」。如果你想完全连续对冲,请填 0

单次回测、多路径回测与历史择优都会在任务启动时读取并冻结左侧头寸方向。 历史择优中的固定 C2C 和全部候选共用该值,不允许同窗跨方向配对。

4.4.1 对冲策略(高级)

字段 含义 选项 默认
对冲策略 调仓触发方式 每日收盘 / 每日固定时刻 / 价格或 σ 带宽 每日收盘
每日收盘保底对冲(重复则跳过) 紧跟策略下拉的公共开关:每个非结算日收盘至少调仓一次,原策略已在同一根 bar 触发时合并为一次,不重复计成本;同时控制单次回测与历史择优 开启 / 关闭 开启
  • close_to_close:有真实时间戳时按每个交易 session 的最后一根 bar 调仓;含夜盘时夜盘、跨午夜和次日日盘属于同一交易日。无时间戳模拟路径回退到 steps_per_day 取模。
  • fixed_times:每天在用户输入的 HH:MM 时刻调仓。Wind 会按当前代码的交易时段自动跳过明确处于休市区间的目标(例如无夜盘品种的 23:00);其余生效时刻仍须由每个纳入回测的交易日组完整覆盖,任一交易日缺 bar 都会明确报错。模拟、Wind 日频和仅日期 CSV 不受支持;CSV 因没有品种交易时段元数据而保持全部目标严格校验。
  • hedge_band:相对上次实际对冲价达到统一带宽后调仓。三个输入框联动,最后编辑的字段是权威输入:

每日收盘保底对冲(重复则跳过) 不是第四种策略,而是适用于以上所有 GUI 策略的 公共规则——所以它排在策略下拉右侧,而不是混在某个策略的专属参数里;历史择优页 不重复提供该开关,任务启动时直接冻结左侧控制的当前值。 开启后,每个非到期、非敲出交易日组都会在最后一根 bar 保证一次 Delta 对齐:

  • 原策略若已在同一根 bar 触发,两个条件会合并为一次调仓。例如 close_to_close、包含实际收盘时刻的 fixed_times,以及恰好在日末越过带宽的 hedge_band 都不会产生第二笔重复交易、成本或触发计数。
  • 到期或敲出所在的末 bar 优先执行终止处理,直接把标的头寸平至 0,不会先做 一次收盘 Delta 对齐后再平仓。
  • 对 CSV / Wind 日内行情,“收盘”是交易 session 分组后的最后一根真实 bar, 不是写死的 15:00;因此可适配夜盘、跨午夜或数据源实际尾 bar 时间。
  • 对没有真实时间戳的模拟路径,引擎按最终采用的 steps_per_day 划分交易日,并把 每组最后一个步长视为日末。

关闭该开关时,各策略只使用自身的触发条件,行为与原有回测口径一致。

GUI 只保留以上三种策略,默认使用 close_to_close。旧的 FixedFreqStrategyhedge_freq 参数仅为后端 API 兼容入口,不出现在 GUI, 也不参与 GUI 的多策略比较或历史择优;调用方仍可在 Python API 中显式使用。

带宽字段 同一参考点上的换算关系
绝对间隔 absolute
相对间隔 relative = absolute / S_ref,界面中 0.01 = 1%
日波动 σ 倍数 k = relative / (sigma_annual / sqrt(ANNUAL_DAYS))

编辑任意一项并按回车或移出输入框,会根据当前 s0、年化 sigmaANNUAL_DAYS=243 反推另外两项;修改 s0sigma 也会自动刷新。运行前 会再次严格校验当前可见输入,避免沿用隐藏的旧值。σ 表达模式还可选择 implied / realized 及历史波动率回看期(至少 2 日)。 单次回测的 σ 来源与回看期只服务当前固定间隔策略,不会渗进历史择优——择优页的 候选带宽固定使用输入波动率。候选梯度在独立的“历史择优”页配置,两套页面状态不会 互相遮挡或误触发校验。

三栏联动表示它们在当前参考价格和波动率下对应同一阈值,并不保证整条动态路径 严格等价。普通单次回测会保留用户最后编辑的执行单位:绝对口径保持固定价差, 相对口径相对上次实际对冲价计算,σ 口径则沿用所选 σ 来源和历史波动率回看期动态 计算。改变执行单位可能因此得到不同的后续触发时点。

CSV/Wind 若把期权参考价 S_ref 伸缩到真实首价 S_real,绝对间隔会同步乘以 S_real / S_ref;相对间隔和 σ 倍数保持不变。历史择优会把固定间隔候选统一到 日波动 σ 坐标,并在严格历史区间的各代理段上按 σ 策略执行,因此不依赖某个绝对价格水平。

4.4.2 bar 粒度与每日 bar 数(intraday)

steps_per_day 仍是回测引擎的重要内部量:它决定日内时间步长、模拟路径长度、 波动率年化口径以及固定间隔策略检查价格越界的频率,但不再作为所有数据来源 共用的顶部输入项。

数据来源 GUI 展示与取值规则
模拟 在模拟参数区显示「模拟采样 bar/日」,可选 1 / 4 / 48 / 240;默认 1
CSV 不显示可编辑的每日 bar 数;仅日期索引按 1,日内 DatetimeIndex 按真实交易日组自动推导并校验
Wind 日频 由「行情采样粒度」自动选择或手工指定;不显示可编辑的每日 bar 数,固定为 1
Wind 分钟 由「行情采样粒度」自动选择兼容值或手工指定;实际每日 bar 数按返回的时间索引和品种交易 session 自动推导并校验

模拟下拉提示为 1=日频 / 4=60分 / 48=5分 / 240=1分。这里的分钟说明是 标准日盘的直观对应关系;真实行情可能包含午休、夜盘、跨午夜或尾 bar,因此 CSV / Wind 始终以实际交易日组为准,不接受 GUI 手工覆盖。固定间隔策略只会在 已有价格 bar 上检查是否越界,所以模拟采样越密,越能捕捉日内触发。

跨日 bar 上 option.step_forward 被调用;日内 bar 仅用 _bumped_copy(_intraday_elapsed=…) 临时评估 Δ,不污染 option 内部状态(hedge_backtest.py:531-532)。年化口径 dt_bar = 1 / (ANNUAL_DAYS * spd),已实现波动率年化因子 ANNUAL_DAYS * spd

4.4.3 滑点

字段 含义 单位 默认
滑点 (bps) slippage_bps,单边滑点(万分之一为 1 bps) bps 0

实现(hedge_backtest.py:515-596):

  • 买入(trade > 0)成交价上浮 bps × 1e-4;卖出下浮。
  • TC 同时计入 |trade| × s_exec × tc_rate|trade| × S × sl_rate 两部分(手续费和滑点不重复计)。
  • Day 0 建仓也按这个口径收滑点(hedge_backtest.py:494-507)。

4.5 运行按钮区

按钮 行为
▶ 运行回测 主流程:构建期权 → 生成/读取价格 → 执行 HedgeBacktest.run(),模拟模式 n_paths>1 时还会跑 run_multi 并显示进度条
+ 保留当前结果到对比 普通回测成功后启用;为当前结果命名并复制一份轻量对比快照。每次成功结果只能保留一次,删除后可重新保留
⚖ 回测结果对比 (N) 打开当前会话的结果池;N 为已保存数量。点击“显示”列会立即用缓存结果刷新曲线、排名与详情,不会重新运行回测。产生方式 列区分手工回测、优选验证与分段重放
📊 绘制结构图 不依赖回测结果,扫描 S 在 [s0×(1−r), s0×(1+r)] 区间,展示 Price / Δ / Γ / ν / Θ 曲线
扫描 ±% 结构图扫描幅度,默认 30(即 ±30%),合法范围 (0, 100)
点数 扫描点数,默认 31,合法范围 5~201

结构图默认会限制 nPath ≤ 20000 以加速扫描(见 _structure_worker),原值若大于 20000 仅在结构图时生效,回测主流程仍用原值。

结构图视角与 头寸方向 联动:卖方(position=1)时 sign=-1,所有曲线乘以 sign(见 _show_structure),方便直观判断卖方账户的盈亏方向。


5. 一次完整回测的操作流程

以「卖出 22 日香草 ATM Call、模拟 500 条路径」为例:

  1. 打开 期权类型大类香草期权 (Vanilla)子类型 自动 Eu
  2. 期权参数 区保留默认(s0=100, K=100, T_days=22, sigma=0.18, 方向=看涨 (Call),r=0.03, q=0.03)。
  3. 回测设置
    • 数据来源模拟
    • 种子 42
    • 已实现波动率 留空(=隐含)
    • 模拟路径数 改为 500
    • 模拟采样 bar/日 1交易成本率(%) 0.01
    • 头寸方向卖出 (Sell)(内部 position=1)
    • 交易数量 100合约乘数 0(连续对冲)
    • 对冲策略 close_to_close滑点 (bps) 0
  4. ▶ 运行回测。底部状态栏显示「正在运行回测…」,进度条先显示不定模式,进入 MC 阶段后切换为确定模式(带 蒙特卡洛模拟: x/500 文字)。
  5. 完成后状态栏提示当前结果可保留,并自动跳到「回测摘要」Tab。
  6. + 保留当前结果到对比,输入名称,例如 每日收盘 · 基准
  7. 修改 对冲策略 或策略专用参数,再次运行并保留,例如 固定间隔 · 相对1%
  8. ⚖ 回测结果对比 (N);在结果池“显示”列勾选两条或更多快照,曲线、排名、摘要卡和相对差异会即时更新。切换勾选不会调用回测引擎,旧结果也不会被最新一次运行覆盖。

预期看到:

  • 回测摘要:含期权初始/到期价值、对冲盈亏、MtM 盈亏、对冲误差、Greeks 表格;底部追加蒙特卡洛分布(均值/分位数/盈利概率/RV 统计)。
  • 对冲图表:6 宫格曲线。
  • 波动率分析:4 宫格,含 RV vs IV、价差填色、日收益直方图叠正态。
  • 盈亏分布:直方图 + 散点,含线性拟合斜率(盈亏 vs 波动率价差)。
  • 每日明细:只显示触发对冲的记录,并可将同一筛选结果导出 CSV。

6. 结果解读

6.1 回测摘要(_show_summary

  • 单路径盈亏分解
    • 标的对冲盈亏 = Σ H[i-1]·(S[i]-S[i-1]) —— 标的腿浮动盈亏
    • 期权 MtM 盈亏 = Σ -position·(V[i]-V[i-1])·quantity —— 期权方价值变动(卖方视角下取负)
    • 累计交易成本 = Σ TC[i]
    • 对冲误差 = position·V[0]·quantity + Σ hedge_daily − Σ TC − position·V[-1]·quantity,对应 hedge_backtest.py:620
  • 波动率分析成交隐含波动率 直接来自 option.sigma已实现波动率 用全样本 bar 级对数收益年化。
  • Greeks 统计:表格列出实际期权仓位的 Delta/Gamma/Vega/Theta/Rho,即 portfolio Greek = -position × quantity × raw Greek;底层仍保留每份多头 claim 的 raw Greek 字段供定价与兼容调用。

6.2 对冲图表(_show_chart

6 宫格,从左上到右下:标的价格、Delta 对冲持仓目标 vs 实际持仓、组合 Gamma、组合 Vega、组合 Theta、累计盈亏(绿/红填色表示正负)。

Delta 持仓目标 = delta × quantity × position,实际持仓 sharesmultiplier 取整影响,会出现明显阶梯。

6.3 波动率分析(_show_vol_chart

  • (1) 滚动 RV(20d) vs IV,填色表示 RV 与 IV 大小关系(红=RV>IV)。
  • (2) 累计 RV 与 IV 的收敛对比。
  • (3) 价差时间序列 = IV − 滚动 RV,正值(绿色)表示卖方相对有利。
  • (4) 日收益直方图叠 IV 正态、RV 正态;其中日 IV σ = IV / sqrt(ANNUAL_DAYS) 转换为百分比(ANNUAL_DAYS=243,见 pricing/constants.py)。

6.4 盈亏分布(_show_dist_chart

simulate + n_paths>1 时有内容,否则显示提示文字。

  • (1) 总盈亏直方图 + 均值线 + 5% VaR 线
  • (2) 对冲误差直方图
  • (3) 已实现波动率分布
  • (4) 总盈亏 vs (IV−RV) 散点 + 线性拟合(斜率正常应为正:RV 比 IV 低越多,卖方赚得越多)

6.5 结构分析(_show_structure

顶部有 STRUCTURE_DOCS 中的中文 payoff 描述 + 当前参数摘要;下方 6 宫格扫描曲线,含 K/H/KI/E/P 等关键价位的虚线标记(颜色区分)。视角受头寸方向控制:卖方(position=1)时 sign=-1,买方(position=-1)时 sign=1,所有曲线均乘以 sign,方便直观判断。

6.6 每日明细(_show_table

Treeview 只保留 hedge_triggered=True 的生命周期事件:Day 0 初始建仓、 策略触发、公共收盘兜底,以及到期或敲出平仓。策略已触发但目标持仓未变化时仍会 保留,因为这里展示的是“触发”而不是“实际成交”;普通未触发 Bar 不展示。

表格在原 13 个行情、Greeks、持仓与盈亏字段前增加 触发来源。日频保留原交易日 序号;日内行情保留原始 Bar 序号及完整的 YYYY-MM-DD HH:MM 时间,避免同一交易日 的多个触发点混淆。盈亏字段仍是该触发 Bar 的当期值,并非两次触发之间的区间汇总。 导出 CSV 使用表格的同一份筛选结果,以 utf-8-sig 编码保存(Excel 友好)。

6.7 回测结果对比(会话结果池)

普通回测成功后,+ 保留当前结果到对比 会复制一份只包含日级聚合、成交统计 和累计曲线所需字段的轻量快照。它记录的是本次运行时的策略、参数和实际行情, 所以随后修改左侧输入不会反向改变旧结果。快照只存在于当前应用会话,关闭程序 后清空。

结果池支持:

  • 显示 / 隐藏:点击“显示”列的 ☐ / ☑。每次切换都会同步刷新图表、摘要卡、 排名和详情;只读取已完成快照,不启动线程、不重新读取 Wind/CSV,也不重跑回测。
  • 重命名:结果使用稳定 ID 保存,改名只改变展示标签,不会丢失勾选状态或数据。
  • 固定基准:买卖方向分组;当前方向最早保留的 close_to_close 快照固定为 基准。选择另一方向结果时会切换整组,并自动带入该方向基准;买入与卖出结果 不能进入同一排名。
  • 删除:从当前会话移除快照;删除固定基准后,下一条已保留的 close_to_close 自动接替。若没有可接替结果,绝对排名与曲线仍保留,但相对指标 显示 并提示先保留每日收盘结果。
  • 全选 / 清空:“全选”只选择当前聚焦结果对应的同方向快照,相反方向不会 混入;“清空”可以连同固定基准一起清空显示。之后再选择任一候选会自动带回 同方向基准。选择 0 条时显示操作引导;只有基准 1 条时展示自身指标并提示再选一条。
  • 产生方式产生方式 列显示这条快照是怎么来的 —— 手工回测优选验证·<周期>分段重放。它读的是快照上的结构化 origin 字段, 不是名称前缀,因此重命名不会让来源丢失。优选验证的快照还在 origin_meta 里记录来源周期、当时排名、改善幅度和批次编号;这些只作溯源,不参与本页 任何排名计算,本页指标始终由快照自身的单路径结果算出。

曲线配色与 策略优选 页共用同一份会话级登记表:同一个策略(按策略类型加其 决定性参数识别,固定间隔 按换算后的 σ 归并)在两页拿到相同颜色和标记, 每日收盘 基准固定占用同一色位。因此在优选页看到的某条候选曲线,验证入池后 在本页仍是同一个颜色,可以直接对应起来看。

固定间隔快照会同时记录输入单位,以及期初参考点对应的绝对价格、相对百分比、 日波动 σ 倍数,方便直接识别不同带宽参数。这些换算值是参数注释,不代表三种 执行口径在整条路径上严格等价。不同策略及其专用参数属于对比对象,本身不会触发 “不可比”警告。

系统先强制要求头寸方向一致,再核对三组上下文:实际价格与时间索引、期权结构 参数、数量 / 乘数 / 成本 / 滑点 / 日级聚合口径。除方向外的上下文全部一致时 显示绿色确认;任一组不同时仍允许把曲线并列展示,但会说明排名只适合作为场景 对照,不能把全部差异归因于对冲策略。

顶部四张卡片和排名表口径如下:

项目 含义
固定基准 当前头寸方向最早保留的 close-to-close 快照;用户改名不会改变其稳定 ID 或基准身份
基准日净 PnL RMS close-to-close 每个真实交易日净 PnL 的均方根,金额口径、已扣交易成本
当前最优 所选快照中 RMS 最低的结果;副标题显示它相对固定基准的改善幅度
基准总成本 / 再触发 close-to-close 的累计交易成本与除建仓、到期外的触发次数;副标题另列实际成交次数
较每日收盘改善↑ `(基准 RMS - 本行 RMS) /
期末净 PnL 最后一个真实交易日结束时的累计净 PnL
再触发 / 成交 前者不含初始建仓和到期处理;后者只统计持仓确实变化的成交,包含建仓与到期平仓
最大回撤 日度累计净 PnL 从历史高点到后续低点的最大金额回撤
换手额

累计净 PnL 曲线先把 bar 级损益按真实交易 session 汇总,并已扣除成本。在排名表 中 close-to-close 基准使用虚线,单选一行会突出对应曲线,并在详情区显示它相对 固定基准的 RMS、成本、净 PnL、最大回撤、再触发 / 成交和换手额差异。评分排名 本身仍按 RMS 从低到高排列,固定基准不一定是第一名。

较每日收盘改善↑ 只属于本页(结果对比),策略优选 页已不再展示同名指标—— 那一页排名看的是增量收益 / 增量信噪比,两者既非同一统计量也非同一样本:本页 分母是 |基准 RMS|、样本是一条单路径。详情区列出的是原始差值(本行 - 基准),因此对 RMS、 成本、回撤这类越低越好的指标,那里的正值仍代表更差 —— 排名列表头的 ↑ / ↓ 标记说明每一列该往哪个方向读。

这个页面只读取和管理已经完成的普通回测快照。历史候选参数、严格区间代理段和历史排名 不会出现在结果池中,也不会与普通快照直接混排;两类结果的统计样本和用途不同, 强行放进同一排名容易造成误读。

6.8 ◷ 历史择优(独立一级页面)

◷ 历史择优 与单次回测、回测结果对比平级,拥有独立的参数区、运行状态和结果 容器。它仅接受 CSV / Wind 真实行情;选择模拟来源时页面会阻止启动任务,也不会 生成“历史推荐”。它复用同一回测引擎和评价指标,但不会读取回测结果池作为搜索 输入。

6.8.1 候选参数

参数组 用途
基准环境摘要 显示历史行情源、日期区间、期权结构、头寸、数量、成本、滑点与左侧收盘兜底状态,便于核对搜索口径
Wind 历史范围 独立指定分析截至日;自动模式取得 max(L_selected) 个评分交易日,并额外请求一个 Day 0 收盘锚点。本页 σ 恒取左侧输入的波动率,因此 realized-HV 预热 V 恒为 0,自动范围不会为预热多留一天(勾选框文案同样只写「+ Day 0 锚点」)。代理期限 T 不扩张证据区间;默认全选时 max(L_selected)=243
历史行情采样粒度 独立选择「自动(推荐)」或高级手工值;一次搜索中的全部候选共享最终实际粒度
候选策略 每日收盘固定作为 C2C 比较基准且不可关闭;必须至少再启用固定时刻或固定间隔中的一种;仅供后端兼容的固定频率策略不参与
收盘兜底规则来源 直接读取左侧唯一的「每日收盘兜底对冲」开关,本页不重复提供控制;启动任务时冻结当前值并应用于全部候选
固定时刻候选 配置一个或多个 HH:MM;默认按同一交易日的业务顺序排列为 23:00,11:30,15:00(前一自然日夜盘 → 次日日盘)。Wind 自动跳过该代码休市时刻,只有完整覆盖其余生效时刻的真实日内样本才参与该案例
固定间隔候选 (σ) 逗号分隔的常见带宽梯度,默认 0.5, 0.75, 1, 1.5, 2
加入当前回测带宽 可选地把单次回测区当前绝对 / 相对 / σ 输入换算成 σ 候选后加入搜索
σ 取值 候选带宽的 σ 恒取左侧输入的波动率,界面不提供来源选项——择优只比带宽倍数,全部候选共用同一个 σ 才可比。引擎仍支持 realized,但不从这里进入
分析周期 近周、近月、近季、近半年、近年五档复选,默认全选且至少选择一项;每档都从同一分析截止日向前严格连续回放最近 L 日,任务与结果只包含已选周期。短周期更贴近当前,长周期覆盖更多市场阶段、更稳健但反应更慢
代理期限与结算 左侧期权期限 T 只决定每个代理段的最长存续期。L<=T 时区间末按剩余期限 MTM;L>T 时区间内按 T 续接,完整段到期结算,最后不足 T 的尾段 MTM
评价口径 候选与固定 C2C 基准只比较双方成功的共同代理段与共同评分日。单一标的序列按增量收益 / 增量信噪比排名(各段日净 PnL 接续后逐日相减);品种历史合约池跨合约无法相加金额,改用各非重叠代理段有界改善按实际评分日数加权。三种状态区分正式历史参考、区间诊断与部分配对

核对基准环境、分析周期和候选配置后,点击 开始策略优选 才会读取真实历史并启动严格区间搜索; 若五档周期全部取消,页面会提示“至少选择一个分析周期”,不会读取行情或启动任务。 单纯切换到该页不会自动运行,也不会改动当前单次回测设置。 开始策略优选 固定在页面顶部一行,与「收起 / 展开候选配置」并排:折叠配置或改动 结果区都不会让它移位,出结果后想改参数重跑不必往回找。任务运行期间该行下方显示 本页自己的进度条(左侧参数栏那条共享进度条同时照常工作)。收起候选配置可释放结果区 空间;候选较多时,周期排名表可垂直滚动。 历史择优启动时会冻结左侧「头寸方向」和「每日收盘兜底对冲」的当前值;固定 C2C 及所有候选都采用相同方向和公共规则。因此策略排名比较的是同一仓位方向下,各自 盘中触发方式叠加同一日末约束后的结果,而不是把兜底本身作为独立候选。

期权期限与择优周期

期权剩余期限 T 和择优周期 L 是两个独立维度,但不再采用“最近 L 日挑选 多个终点、每个终点再向前回看 T 日”的旧口径:

  • L 是实际进入 PnL 评分的严格连续证据区间。近周、近月、近季、近半年、近年 分别采用 5 / 20 / 61 / 122 / 243 个交易日,并全部以同一个分析截止日为右端点。
  • T 是历史代理合约单段的最大期限,只影响定价、Greeks、代理段切分及结算,不会 把行情扩到严格 L 区间之前。
  • L <= T 时只形成一个代理段;它在区间末仍有 T-L 日剩余期限,因此按公允价值 MTM,并平掉对冲腿,不错误套用到期 payoff。
  • L > T 时在严格区间内按 T 续接代理合约;完整代理段到期结算,最后不足 T 的尾段按剩余期限 MTM。结果区会披露代理段总数、到期段数、MTM 段数与实际评分日。
  • GUI 默认勾选五档且至少保留一项,不再生成或传递 5 / 12 / 24 / 36 / 48 的历史终点预算。

这五档是可选的证据 / 稳健性口径,不是统计学置信区间:短周期更能反映近期交易 结构,长周期能覆盖更多行情阶段但对当下变化反应更慢。应优先在同一周期内横向 比较策略,再把本次已选周期的结论并列参考,而不是把不同周期的 RMS 直接混成一个总分。

realized 模式还要在严格区间 Day 0 之前拥有完整 V 日预热,所以正式评分与预热 边界为 N >= L + V;Wind 自动请求还会多留一个 Day 0 收盘锚点。整项自动范围按 max(L_selected) + V + 1个锚点 规划,T 不参与。例如 T=22V=20 的月度择优需要 20 个评分日、20 个预热日和 1 个锚点,而不是旧口径的 T + L + V = 62 日。implied 模式取 V=0;只有勾选近年时,最长评分区间才使用 243 日。手工范围或实际返回的完整交易日不足时,系统可保留已有共同代理段结果用于诊断,但不把它 标记为完整周期的正式历史参考。

Python API 为旧调用方保留必要兼容字段;GUI 主流程只传入已选 lookbacks, 不传 target_endpoints,结果也不再展示终点预算、终点间隔或相邻窗口重叠率。

历史页的 Wind 设置与单次回测相互独立。常规模式只需指定「分析截至日」,系统 会以 max(L_selected) + V 个评分/预热交易日并加一个 Day 0 锚点为目标自动向前 扩展起始范围:max(L_selected) 取已选周期中最长的严格证据区间,realized σ 再增加区间前 V 日预热。近年档的 243 是项目统一的年度交易日口径,不是简单向前减去 243 个自然日。高级模式允许手工 覆盖起止范围和采样粒度,但手工范围不足时会显示区间不足或区间诊断,不会伪造 已选周期的正式推荐。

Wind 期货代码路由

历史择优不会把所有期货代码都当成连续品种:

  • P2609.DCE 这类带交割年月的代码是具体合约。系统只使用该合约返回的历史, 不会因季度或年度样本不足而自动扩展到 P 的其它合约。
  • P.DCE 这类“品种 + 交易所”代码是品种历史合约池。系统查询截止日以内的 Wind trade_hiscode 逐日历史主力映射,只加载最长已选周期证据范围中真实出现过的 具体合约;不会按交割月份猜测,也不会加载截止日之后才成为主力的合约。
  • 严格区间只使用最近 L 日内的主力映射;区间外最多为首个 Day 0 锚点与 realized σ 的 V 日预热加载必要行情,不会因代理期限 T 向更早日期扩张。
  • 每个代理段会披露实际具体合约;跨合约图表使用共同代理段,并按安全名义金额 归一化,避免高价合约仅因金额 PnL 更大而支配显示。

若一个历史合约加载失败,涉及该合约的代理段会被标为失败,其它可用代理段继续 运行。周期只有在严格区间及共同代理段覆盖完整时才形成正式参考,否则保留为 “区间不足,仅诊断”。分析日盘中残组只有在行情中确有 尾部残组证据时才自动回退到最近完整交易日;整个当前主力缺行情不会被静默回退成 以前一合约为截止点的正式结论。

历史「自动(推荐)」会综合全部已启用候选决定统一请求粒度:仅有每日收盘候选时 日频即可;包含固定时刻候选时必须选用兼容的分钟行情;固定间隔候选会采用推荐的 日内检查粒度。行情读取完成后,同一具体合约内的全部候选共享完全相同的实际时间 索引和请求粒度;品种池允许不同历史合约按各自真实 session 推导每日 Bar 数。 页面同时披露请求粒度;手工高级覆盖仍 需满足所有启用候选。固定时刻的自动粒度只考虑该代码交易时段内实际生效的 HH:MM;休市目标不会无谓把请求降到更细粒度。

固定时刻候选在启动时只检查数据是否具有真实日内时间轴,不会因为某个代理段 缺少目标 Bar 而被全局移除。每个 Wind 代码先按自身交易时段区分 “休市目标”和“生效目标”:前者自动跳过,后者由每个历史代理段独立校验。 生效时刻缺 Bar 的失败代理段计入跳过诊断,其他代理段和其他候选继续运行;如果固定 时刻候选最终一个有效代理段都没有,界面会显示首个真实缺失时刻原因,而不是笼统 提示历史长度不足。

固定间隔候选统一使用日波动 σ 倍数作为搜索坐标。候选列表留空表示不使用常见 预设;只有在“加入当前回测带宽”开启时,当前带宽换算值才会加入。默认 / 自定义 候选与当前换算值按 10 位有效数字规范去重,包含当前值在内最多 10 档;超过上限 会在启动任务前提示修改,不会静默截断。所有历史固定间隔候选统一按 sigma 策略执行,因此不同价格水平的历史代理段共享同一波动尺度。

绝对、相对和 σ 的换算只表示当前参考点上的阈值对应关系。普通单次回测仍按用户 最后编辑的原始单位执行;历史页的 σ 搜索不会悄悄改变单次回测参数。由于三种口径 在路径推进时更新阈值的方式不同,也不能据此认为它们在所有未来路径上严格等价。

6.8.2 历史结果与单次回测衔接

历史页只在本次勾选的周期中回放严格连续历史区间;可选目录为近周、近月、近季、 近半年、近年,未选周期不会运行,也不会在结果区生成空白占位。每日收盘 close-to-close 始终是固定基准;每个周期先形成 C2C 代理段集合,再对每个候选取 双方都成功的共同代理段和共同评分日。

单一标的序列的主排名依据是“增量”,不是 RMS。 对冲的目的是让 gamma scalping 收益最大,其次才是让这份收益更稳,因此排名比较的是“日内额外调仓相对 「日内不动」多赚了多少”。把各段的日净 PnL 首尾接成一条后逐日相减:

  • 增量收益 = Σ(候选日净 PnL - C2C 日净 PnL)(默认口径)
  • 增量信噪比 = mean(逐日增量) / std(逐日增量)

单位:增量收益是金额,随 数量 × 乘数 线性放大,与「增量成本」列同 单位可直接相减;增量信噪比是无量纲比值,与仓位大小无关。两者都不是百分比。

它形式上与夏普比率同构,但是逐日原始比值、没有年化,因此不要和年化夏普 放在一起比大小(真要换算需乘 sqrt(243)≈15.6)。不年化是有意的:它在这里只 用于同一批候选之间的横向排序,乘一个常数不改变名次,却会诱导人拿它去和别处 的年化夏普对比。列名用「信噪比」而非「夏普」也是为此——夏普一词自带年化约定, 而信噪比本来就是无量纲的原始比值,不会引出这个误会。

两个口径都在回测时一并算出,结果页点表头即可切换,不必重跑。增量信噪比恰好 等于配对 t 值除以 sqrt(n),因此它在衡量“每单位波动换来多少增量”的同时,也 编码了这份增量有多可靠——样本内偶然赚到的一两天大钱会被自身波动稀释。

为什么可以跨段直接把金额相加。 每段都按该段期初价重定基一份新期权,数量与 合约乘数固定,因此段内 PnL 量级正比于该段 S0——价格高的段权重更大。这不是失真: 若按固定手数、每 T 日滚一次,一年下来的增量金额之和就是字面意义上的实现盈亏, 高价段确实产生了更多绝对损益。同一段内所有候选与 C2C 共用完全相同的期权、数量、 乘数与成本率,因此相减是同口径的。

该结论只在同一标的、同一次运行内成立。跨合约时每手名义金额会变,不再是"固定 手数交易同一个东西",因此品种池模式改用逐段改善率加权而非金额求和。跨运行(不同 截止日或周期)的金额总额同样不可直接比较——段构成变了。

实测参考:510050.SH 近一年 12 段的 S0 区间为 2.846~3.235(1.14 倍),段权重相对 均值偏离最大 ±7%,远小于候选之间的增量差距(约 2 倍量级)。

品种历史合约池只能用改善率排名。 跨合约不能直接把金额 PnL 相加,因为不同 合约的价格与金额尺度可能不同,所以该模式不产出增量指标。系统在每个非重叠代理 段 j 内计算日净 PnL RMS 的有界改善 I_j

I_j = (C2C RMS_j - 候选 RMS_j) / max(C2C RMS_j, 候选 RMS_j)

再按该段实际评分日数 d_j 加权:I_L(pool) = Σ(d_j × I_j) / Σd_j

因此品种池结果表里“增量收益 / 增量信噪比 / 增量成本”三列都显示 ,两列表头 也不可点(点了也只是静默回退到同一个值),周期说明会写明本次实际按改善率排名。 若改善与 C2C 持平,固定优先 C2C,避免为无历史改善的候选增加复杂度。

候选必须覆盖该周期全部 C2C 基准代理段和评分日,才可以进入正式历史择优;历史区间 本身不足但候选与现有 C2C 代理段完全配对时,只给“区间诊断”;候选自身缺 Bar 或 失败造成的部分配对只能展示诊断明细,不能成为周期领先者。

排名表展示的四列是增量收益、增量信噪比、增量成本、最大回撤;RMS 得分与代理段 胜率已收敛为选中行的详情串,不再占表宽。周期说明披露 T / L / 实际评分日 / 代理段数 / 到期段 / MTM 段 及终止口径。品种池还披露实际 参与该周期评分的具体合约。只有至少一个非 C2C 候选完成正式共同代理段比较时,才产生“历史最优参考”; 若全部正式候选都不如 C2C,结论会明确显示“每日收盘(基准最优)”,含义是历史 区间支持维持 C2C。代理段胜率表示候选 RMS 严格低于同代理段 C2C RMS 的段数占比; 平局不计胜。所有结论 仅作历史参考,不代表未来表现。

每个已选周期先展示完整策略排名数据表,图表放在数据表下方,两者之间是一条可拖动的 分割线:结果页整体不再滚动,顶部的周期条与结论卡固定,剩余高度由用户在排名表和 图表之间自行分配。初始分割按 45% 摆一次,随后由 tk 按两格的请求高度微调,实际落 在四成上下(约六行排名 + 一块完整图表);拖到任一端都有下限——排名表不少于 150px,图表那一格不少于 280px(扣掉指标条与下钻条后仍留约 160px 绘图高度,坐标轴 和图例不会糊成一团)。窗口缩放时两格按比例同步伸缩,用户拖过之后位置完全由用户 掌握。排名表内部仍有自己的滚动条,图表随格高自适应重绘。

初始分割只摆一次,之后不再跟随:摆放分割线会让图表重绘、改变该格的请求尺寸、 外层 grid 重排后又把分割区高度改掉二十来像素,无条件响应这个回波会形成 摆放 → 重绘 → 重排 → 摆放 的闭环——实测单次渲染里分割线被摆放 300 多次,界面一 帧都画不出来,内存被绘图缓冲区反复分配撑到 GB 级。所以只有幅度超过 15% 的高度变化 (真的拉了窗口)才会重新摆放,另有一个硬性次数上限兜底。排名行保持单选,用于查看详情、应用策略或执行 当前路径验证;勾选与其分离,通过排名表首列 勾选 选择候选。该勾选同时决定图表 叠加哪些曲线,以及“当前路径验证”按批量还是单条执行。切换到某个周期时,默认勾选 排名前三名里存在共同代理段的候选——数的是名次而不是个数:C2C 基准也占一个 名次却不可勾选(它在图上恒在),所以基准排进前三名时默认只会勾上两个候选,不会 为了凑够三个再往下多抓一个名次;名次窗口内若有候选与已选没有共同代理段,跳过它 即可,同样不向下顺延。清空勾选 清除全部,之后可逐项调整(清空后不再自动恢复 默认,需要重新运行或手动勾选)。 C2C 不可取消,候选最多同时显示八个,以避免曲线和图例失去辨识度。候选的颜色和 标记来自与“回测结果对比”页共用的会话级登记表,因此同一策略验证入池后在对比页 仍是同一个颜色(见 6.7)。图表提供一个与 排名证据一致的主视图和两个代理段诊断视图:

  • 完整 L 日累计路径(默认):按严格区间的时间顺序拼接全部非重叠共同代理段 的日 PnL,再从第一天开始累计;因此完整覆盖时横轴正好是 1..L,与周期主排名 使用的历史证据一致。代理段边界用竖向虚线标记。
  • 单代理段路径:从“代理段”下拉框选择一段,同时比较全部已勾选 候选与 C2C 的累计路径;口径可切换为净 PnL、成本前 PnL 或成本,并满足 净 PnL = 成本前 PnL - 成本
  • 多代理段中位路径:按代理段内交易日对齐,绘制中位路径;至少有 两个共同代理段时显示 P25/P75 阴影。为避免多层色带遮挡,阴影只显示 C2C 和当前 主候选,其他候选仍显示中位线;提前敲出 / 结算后累计值保持不变直至最长代理段, 避免后半段只剩未敲出代理段;阴影是代理段的描述区间,不是置信区间。

所有比较只使用同严格周期、同一内部代理段标识、段内长度一致且 C2C 与全部已勾选 候选都成功的共同交集;切换净 PnL、成本前 PnL 或成本时会按该曲线口径重新求交集。 固定时刻缺 Bar 的代理段不会补零或前向填充;若失败使共同交集不足 L 日,完整 路径会同时显示实际共同日数与目标 L,不能把缺失日误读为零损益。单代理段路径与 汇总 RMS 均为当前合约数量与价格水平下的金额口径;品种池跨多个具体合约时,完整 路径和多代理段中位路径都使用各段 PnL / (S0 × multiplier × |quantity|) 安全归一化。若 S0、乘数或数量不能形成 有限正分母,或同代理段候选与 C2C 的分母不一致,界面会停用相关聚合图并提示改用 单代理段路径,不会回退到跨合约原始金额曲线。

排名表仍保留每个策略自己的全部配对代理段统计。当已勾选策略的共同交集小于其中 任一策略的独立配对集合时,图表会明确提示“与排名表样本口径不同”;此时应以共同 代理段路径比较曲线,以排名表评估各策略各自能覆盖的完整历史,不应混用两个分母 解释结果。

结果区顶部是一张结论卡,位于排名表之上:它显示排名表当前选中的那一行——策略 名、该行是否本周期最优(或基准 / 数据不足仅供参考)、以及增量收益、增量信噪比、 胜段占比、可比样本四个数字(品种池模式下增量口径不可用,前两项自动换成损益波动与 每日收盘原值)。刚出结果时表格默认选中 rank 1,因此卡片默认显示的就是本周期推荐; 用户改选别的行,卡片随之切换。卡片绑定选中行而不是固定显示周期冠军,是因为右侧 两个动作按钮作用的对象就是选中行,两者必须指同一个策略。跨周期一致性收敛为周期 切换条右端的一枚 pill(一致 / 不一致),完整措辞在悬停提示里。

历史排名不会直接写入回测结果池。“换到当前环境重跑”的两个按钮在结论卡右侧,历史 证据回放因为要先在图上选段,仍留在图表下方:

  • 加载分段到展示页(图表下方):从“历史分段”下拉选择该候选跑过的某一段历史, 按原始行情切片重跑并把逐 bar 结果渲染到回测摘要 / 对冲图表 / 每日明细 / 波动率分析。
  • 仅回填参数:把策略类型及其专属参数回填到单次回测区,不立即运行, 便于用户检查行情、期权和成本口径后再执行。
  • 当前路径验证:使用当前单次回测环境执行推荐策略;只有回测成功后,才把它 作为普通快照保留到“回测结果对比”页。此时它与其它快照使用相同的单路径指标, 可以正常勾选比较。执行范围由排名表首列的勾选决定:没有勾选时只验证当前选中 的那一行;有勾选时按排名顺序逐条批量验证,并在缺同方向每日收盘快照时先补跑 基准。按钮下方的说明会随勾选实时写明这一次点击将要执行的范围。入池的快照会 带上结构化来源(优选验证·<周期>)以及来源周期、当时排名与批次编号。

批量验证结束后不会自动跳页,也不再用模态框报错 —— 当前选定的周期和勾选状态 保持原样。结论写在结论卡底部的结果条里:全部成功显示绿色的完成条,有失败时 显示黄色的完成条并提供 失败详情 按钮按需展开原因。结果条上的 查看结果对比 会切到结果对比页,并只勾选这一批刚验证出来的快照(缺同方向基准时自动带入), 所以跳转由用户主动发起,且落地即是这批结果的视图。结果池本身在批量结束时就 已更新;如果对比页当时可见,会同步刷新。

这些动作替代历史页内嵌的“当前期限路径”辅助结果,避免把严格区间汇总与单条 路径结果放在同一容器中。若一个有效样本都无法形成,或 CSV / Wind 历史读取失败、 滚动推荐计算抛出异常,整个任务会失败。系统会优先显示缺失具体合约、缺少主力日 或固定时刻等首个真实失败原因;只有没有更具体诊断时才提示扩大历史区间。


7. 常见问题与坑

7.1 启动相关

  • ImportError: WindPy:仅在选 Wind 数据源时触发(pricing/wind_data.py:125-129)。模拟和 CSV 模式不需要 Wind。
  • ModuleNotFoundError: tkinter:部分 Linux 发行版默认不带 tkinter,安装 python3-tk(Debian/Ubuntu)或 python3-tkinter(RHEL)即可。Windows / macOS 官方 Python 自带。
  • SyntaxError: invalid syntax (match/case):Python 版本低于 3.10。

7.2 价格序列长度不足

报错示例:价格序列长度不足:期权剩余 22 日 x steps_per_day=1 需要 23 个价格点,实际仅 18 个。

steps_per_day 是引擎最终采用的每日 bar 数:模拟来源取自「模拟采样 bar/日」, CSV / Wind 则由真实时间索引和交易 session 自动推导。Wind 的「行情采样粒度」 只决定请求口径,不直接写入 steps_per_day。商品期货的 30/60 分钟 Bar 会按 夜盘、上午休市前后和下午等连续交易段分别向上取整,不会用总分钟数简单相除。 CSV / Wind 模式下需要保证 完整交易日组覆盖的期限不少于期权剩余期限;可以放宽日期区间、补全日内行情, 或调小 T_days / T,而不是手工填写每日 bar 数绕过校验。

7.3 历史价 S_ref 必须 > 0

CSV / Wind 模式下若期权 s0 设为 0 或负数,会触发 _rescale_option_to_real_s0 抛错(hedge_backtest.py:201)。请填入正的参考价。

7.4 固定时刻提示行情不是日内数据

固定时刻策略要求每个交易日至少两根 bar。Wind 会自动跳过当前代码明确不交易的 目标时刻;其余生效 HH:MM 必须逐交易日覆盖。建议将「行情采样粒度」保留为 「自动(推荐)」;若使用高级手工粒度,应选择能落在全部生效目标时刻上的分钟 bar。CSV 无法可靠识别品种休市时段,因此需提供第一列为完整日期时间且覆盖全部 配置目标时刻的日内行情。

7.5 蒙特卡洛盈亏分布 Tab 一片空白

只有 数据来源 = 模拟模拟路径数 > 1 时才会触发 run_multi,否则该 Tab 只显示提示文字。

7.6 结构图扫描 nPath 被截断

_structure_worker 会把 nPath > 20000 强制截断为 20000 以加速扫描。这只影响结构图,不影响主回测中真实的 nPath

7.7 HV 回看期 N (日) 样本不足

普通单次回测为兼容旧行为,在有效收益不足时会回退到 implied σ。历史择优不再提供 σ 来源选项(候选带宽恒用输入波动率),因此该页不会出现 HV 预热不足的问题,也不需要 为此额外拉取预热区间。

7.8 multiplier 默认值 = 5

GUI 与底层 HedgeBacktest.__init__ 均默认 合约乘数 = 5hedge_backtest.py:341)。新手第一次跑可能看到「Delta 与实际持仓」图上有明显阶梯——这是按 5 取整造成的,并非 bug。如需连续对冲请改为 0

7.9 勾选已保存结果会不会重新回测?

不会。保留结果时,系统只调用 summarize_strategy_resultresult_daily_frame 缓存指标和日级累计曲线;之后的勾选直接读取这些缓存, 不会调用 HedgeBacktest.run()compare_strategies()、Wind 或 CSV 读取, 也不会启动后台计算。

7.10 为什么对比页提示口径不同?

系统检测到所选快照的实际行情 / 时间索引、期权结构,或头寸 / 数量 / 乘数 / 成本 / 滑点 / 日级聚合口径不一致。此时仍可并列查看场景结果,但“领先”不能只 解释为策略更好。若要严格比较策略,请保持这些设置一致,只修改对冲策略及其 专用参数。

7.11 对比页那一列为什么由“相对基准”改成了“较每日收盘改善↑”?

因为它和 策略优选 页的同名指标此前符号相反:对比页用 (本行 - 基准)/|基准|(负值更好),优选页用有界改善(正值更好),两个相邻页面 读法互斥,极易把“更差”看成“更好”。现在对比页统一成正值更好,与本页顶部“当前 最优”卡片的算法一致。数值大小没变,只有符号翻转;两页仍是不同统计量(分母与 样本不同,见 6.7),统一的只是方向。

7.12 已保留结果为什么重启后消失?

当前版本的结果池是会话内缓存,不写入磁盘。这样可避免在未定义文件格式和版本 兼容规则前产生不可恢复的旧快照;关闭应用会清空结果。需要长期留档时,可先用 “每日明细 → 导出 CSV”保存单次明细。


相关源码