FinMarketPy / 外汇曲线与波动率曲面
外汇曲线与波动率曲面:它真正区别于通用回测框架的一块
通用股票回测框架通常不带外汇曲线与波动率曲面。finmarketpy 的 curve/ 包里有三条曲线(即期、远期、期权)加一套曲面与定价器,并且曲面拟合函数、ATM/delta 口径、求解器都能换——这些取值官方 README 一个字都没写,全部藏在 util/marketconstants.py 里。这一页把类清单与参数口径列出来,同时明确一件事:本站未实机运行,不给任何定价结果或波动率数值。
curve/、curve/rates/ 与 curve/volatility/ 源码);示意非官方架构图,不包含任何定价或波动率数值。curve 包里到底有哪些东西
下表由 finmarketpy 0.11.19 发布包解包实读;括号里是关键方法签名,便于你对照自己安装的版本。
| 类 | 文件 | 职责 | 关键方法 | 适用场景 | 注意点 |
|---|---|---|---|---|---|
FXSpotCurve | curve/fxspotcurve.py | 外汇即期曲线的构建与总收益指数 | generate_key()、get_day_count_conv(currency) | 构建即期口径的收益序列 | 不同货币的计息天数惯例不同,由 get_day_count_conv 决定 |
FXForwardsCurve | curve/fxforwardscurve.py | 远期曲线与 roll(展期)规则 | unhedged_asset_fx(assets_df, asset_currency, home_curr, start_date, finish_date, spot_df=None)、get_roll_date(horizon_d, delivery_d, asset_hols, month_adj=1) | 需要把外币资产收益换算回本币、或做远期持有 | 展期时点由常量控制(默认 month-end 提前 5 天),改口径会改变长期收益 |
FXOptionsCurve | curve/fxoptionscurve.py | 期权曲线与期权总收益指数 | get_roll_date(horizon_d, expiry_d, asset_hols, month_adj=0)、generate_key() | 期权持仓的连续化与展期 | 期权 roll 事件默认按到期日(expiry-date),与远期的 month-end 不同 |
FXVolSurface | curve/volatility/fxvolsurface.py | 波动率曲面拟合与取值 | build_vol_surface(value_date)、calculate_vol_for_strike_expiry(K, expiry_date=None, tenor="1M")、calculate_vol_for_delta_expiry(delta_call, expiry_date=None)、plot_vol_curves() | 按执行价或按 delta 取波动率、画曲面/曲线 | 支持 ATM / 10d / 25d 的 call/put 以及对应的 ms(market strangle)变体 |
FXOptionsPricer | curve/volatility/fxoptionspricer.py | 外汇期权定价 | 继承 AbstractPricer;get_day_count_conv(currency) | 对期权做定价 | 定价引擎可选 financepy 或 finmarketpy(见参数口径表) |
FXForwardsPricer | curve/rates/fxforwardspricer.py | 外汇远期定价 | get_forwards_divisor(currency)、get_day_count_conv(currency) | 远期点数与定价 | 点数除数按货币不同(如 JPY 是 100) |
VolStats | curve/volatility/volstats.py | 波动率统计 | adjust_implied_ON_fri_vol(data_frame) | 把隐含波动率按周五/隔夜口径调整 | 含「周末效应」的调整逻辑,跨周末数据要留意 |
AbstractCurve / AbstractPricer | curve/abstractcurve.py、curve/abstractpricer.py | 曲线与定价器的抽象基类 | fetch_continuous_time_series()、construct_total_returns_index()、price_instrument() | 你要自己扩展新品种时的接口约定 | 这两个文件很短,适合作为「怎么接入自有曲线」的入口 |
三条曲线分别解决什么问题
把「即期 → 远期 → 期权」当成三个递进的口径层,选错层会让结论整体偏移。
先问:你要的是现货涨跌还是持有收益
只用即期曲线时,收益来自价格变化;一旦引入远期,就要处理展期(roll)——
FXForwardsCurve.get_roll_date(horizon_d, delivery_d, asset_hols, month_adj=1)决定展期发生在哪一天。官方示例注释提醒过:用现货价算收益会漏掉 carry,这是同一件事的另一面。远期:把外币资产收益换回本币
FXForwardsCurve.unhedged_asset_fx(assets_df, asset_currency, home_curr, start_date, finish_date, spot_df=None)这个方法的用途就是把某一货币资产的收益按未对冲口径换算回本币,适合做「本币视角的组合收益」。参数里要显式给出资产货币与本币。期权:把持仓连续化
期权不能像现货一样长期持有,需要不断展期。
FXOptionsCurve的 roll 事件默认是expiry-date(到期日),另有 month-end / no-roll 可选;默认交易期限是 1M。也就是说,你看到的「期权总收益指数」是一串展期合约拼起来的。曲面:按执行价或按 delta 取波动率
两条取值路径:
calculate_vol_for_strike_expiry(K, expiry_date=None, tenor="1M")按执行价取;calculate_vol_for_delta_expiry(delta_call, expiry_date=None)按 delta 取。另有get_atm_vol()、get_25d_call_vol()、get_10d_put_vol()这类直接取报价点的方法。画出来看一眼
plot_vol_curves()可以直接把曲面切片画成曲线图;FXVolSurface还有get_dom_discount_curve()/get_for_discount_curve()取两边货币的贴现曲线,方便你核对输入是否符合预期。
曲面参数口径有哪些:官方 README 没写的那部分
下表全部来自 util/marketconstants.py 源码实读,是本站认为最值得留档的一页——它决定了你的曲面与别人是否可比。
| 常量 | 默认值 | 可选值 | 影响 | 注意点 |
|---|---|---|---|---|
fx_options_vol_function_type | "CLARK5" | CLARK5 / CLARK / BBG / SABR / SABR3 | 决定用什么函数族拟合微笑曲线 | 换函数族 → 同一批报价出不同曲面,必须记录 |
fx_options_atm_method | "fwd-delta-neutral-premium-adj" | 另可选 fwd-delta-neutral / spot / fwd | 决定「平价值」怎么定义 | ATM 定义不同,整条曲线的锚点不同 |
fx_options_delta_method | "spot-delta-prem-adj" | 另可选 fwd-delta / fwd-delta-prem-adj / spot-delta | 决定 delta 的口径 | 影响 10d/25d 点的取法,进而影响 skew 读数 |
fx_options_solver | "nelmer-mead-numba" | 另可选 nelmer-mead / cg(共轭梯度) | 决定求解方式与速度/精度取舍 | 源码注释写明 numba 版「更快但精度略低」,cg「更慢但更准」 |
fx_options_pricing_engine | "financepy" | financepy / finmarketpy | 决定走哪套定价实现 | 选 financepy 必须先单独装 financepy==0.370 --no-deps |
fx_options_alpha | 0.5 | 数值 | 参与拟合的权重/插值参数 | 改动会改变曲面形状,属于要记录的实验参数 |
fx_options_tol | 1e-8 | 数值 | 求解收敛容差 | 放得太松会静默给出不收敛的结果 |
fx_options_tenor_for_interpolation | ON, 1W, 2W, 3W, 1M, 2M, 3M, 4M, 6M, 9M, 1Y | 列表 | 定义参与插值的期限点 | 源码注释提醒 2Y/3Y 被注释掉了,长端可能不参与 |
fx_options_roll_event / _roll_days_before / _roll_months | expiry-date / 5 / 1 | month-end / expiry-date / no-roll | 决定期权怎么展期 | 与远期的 month-end 口径不同,跨曲线比较时要留意 |
fx_forwards_tenor_for_interpolation | 1W…5Y | 列表 | 远期插值期限点 | 比期权多出 2Y/3Y/5Y |
fx_forwards_points_divisor_* | IDR 为 1、JPY 为 100 | 按货币列表 | 远期点数换算 | 货币写错就会出现数量级错误 |
fx_forwards_roll_event | month-end(提前 5 天) | month-end / quarter-end / year-end / delivery-date | 决定远期展期时点 | 长周期回测里这个口径会显著改变收益 |
currencies_with_365_basis | AUD, CAD, GBP, NZD | 列表 | 计息基准为 365 的货币 | 基准选错会影响贴现与远期定价 |
MarketCred(即 finmarketpy.util.marketcred)覆盖——MarketConstants.__init__ 会逐个比对同名属性;也支持通过 override_fields 字典临时覆盖。研究记录里应写清你改过哪些。要用这块能力,环境上要额外做什么
曲面与定价不是「装完就能用」——它们牵出一个可选依赖与一类典型报错。
| 环节 | 官方做法 | 为什么这样做 | 注意点 |
|---|---|---|---|
| FinancePy(期权定价) | pip install numba numpy scipy llvmlite ipython pandas prettytable,再 pip install financepy==0.370 --no-deps | README 说明:FinancePy 对 llvmlite 一类库有严格版本依赖,直接连带安装容易和别的库冲突 | 必须指定版本 + --no-deps;官方还说 API 变化大,所以要按 README 给的版本装 |
| Numba 编译报错 | README 给出处置:删除 financepy 安装目录下的 __pycache__ 文件夹 | Numba 的编译缓存与当前环境不一致时会报 Failed in nopython mode pipeline 一类错误 | 找到 site-packages/financepy 后删其 __pycache__;这是官方给的解法 |
| 默认定价引擎 | 常量默认 financepy;想完全不用它可切到 finmarketpy | 保留一条不依赖 FinancePy 的路径 | 切引擎会改变数值细节,比较结果前确认两边一致 |
| 求解器选择 | nelmer-mead-numba(默认,快)/ nelmer-mead / cg(慢但更准) | 曲面反解速度与精度需要权衡 | 源码注释直接写明快慢与精度取舍,属可引用的官方说明 |
| 出图 | FXVolSurface.plot_vol_curves() 经 chartpy 输出 | 曲面需要可视化核对 | graph 出不来先查 chartpy 引擎与字体,不是曲面算错 |
| 数据质量 | 曲面输入是市场报价(ATM、10d、25d 等) | 曲面质量由报价质量决定 | 报价缺失或口径不一致时,先修数据,别调参数「凑」出平滑曲面 |
这一块能做什么、不能做什么
把边界说清楚,比给一串公式更有用。
能做:曲面拟合与取值
给定报价与估值日,build_vol_surface(value_date) 拟合出曲面,之后既可以按执行价取(calculate_vol_for_strike_expiry),也可以按 delta 取(calculate_vol_for_delta_expiry),还能直接取 ATM 与 10d/25d 的报价点波动率。
能做:曲线连续化
远期与期权都提供 roll 逻辑,能把不连续的单期合约拼成连续指数;AbstractCurve.construct_total_returns_index() 与 fetch_continuous_time_series() 是这个抽象层的两个入口,适合拿来理解「总收益指数怎么拼」。
不能做:给你现成的市场报价
曲面要用真实的 ATM/10d/25d 报价喂进去。官方示例的报价来自数据商订阅(如 Bloomberg 一类),免费源通常不覆盖这些场外报价。也就是说:能不能做曲面研究,取决于你有没有报价数据,而不是有没有finmarketpy。
不能做:给交易建议或价格预测
本站不给任何期权定价结果、波动率数值或交易建议。曲面是研究工具,不是收益承诺;用它做研究时同样要遵守回测可信度体检那一套口径纪律。
外汇曲线与曲面常见问题
类名与常量取值以 0.11.19 源码为准;本站未实机运行,不给出任何定价结果。
不做期权的人,这一块跟回测有关系吗?
有,但主要在远期曲线。FXForwardsCurve 的 roll 规则(默认 month-end、提前 5 天)决定了长周期外汇回测里的展期时点,官方示例也提醒过「用现货价算收益会漏掉 carry」——这正是远期曲线要解决的问题。所以即使你只做现货趋势策略,也建议看一眼 fx_forwards_roll_* 这几个常量。
曲面拟合该选 CLARK5 还是 SABR?
默认值是 CLARK5,另有 CLARK、BBG、SABR、SABR3。不同函数族对微笑两翼的拟合特性不同,适合的报价环境也不同。本站不给推荐:换函数族必须重做口径记录并重跑对照——因为同一个 delta 点在不同函数族下会给出不同的波动率。以官方实现与你的实证结果为准。
ATM 和 delta 为什么各有四种口径?
因为外汇场外市场的报价惯例本身有多种版本:ATM 可以是 fwd-delta-neutral、fwd-delta-neutral-premium-adj、spot 或 fwd;delta 可以是 fwd-delta、fwd-delta-prem-adj、spot-delta 或 spot-delta-prem-adj。库把默认值定在 premium-adjusted 的版本上(fwd-delta-neutral-premium-adj / spot-delta-prem-adj),并允许你切换。和对手方或文献对比时,先对齐口径,再对比数值。
不装 FinancePy 能用曲面吗?
可以走 fx_options_pricing_engine = "finmarketpy" 这条路径,但要注意默认值是 financepy,也就是说如果你不装 FinancePy 又没改常量,走到定价那一步会失败。官方把 FinancePy 在 2025-03 变成可选依赖并从 pyproject.toml 移除,就是为了让默认安装更轻。以官方文档与你安装的版本为准。
报 “Failed in nopython mode pipeline” 怎么办?
这是 Numba 编译报错,官方 README 给的处置是:删除 FinancePy 安装目录下的 __pycache__ 文件夹。README 还给出了典型路径示例(Anaconda 环境下的 Lib/site-packages/financepy)。如果删了仍报错,再按报错页的依赖段排查版本组合。
有没有办法在免部署路线里做曲面?
没有对应技能。本机 本机技能的覆盖范围是行情/财务/技术面/出图/回测指标,没有外汇期权、波动率曲面、曲线库这一类能力。这一点在对比页也如实标明——不宣称能用技能替代这块。要做曲面研究,目前只有自建 finmarketpy + 自备报价数据这一条路。