FinMarketPy / 外汇曲线与波动率曲面

外汇曲线与波动率曲面:它真正区别于通用回测框架的一块

通用股票回测框架通常不带外汇曲线与波动率曲面。finmarketpy 的 curve/ 包里有三条曲线(即期、远期、期权)加一套曲面与定价器,并且曲面拟合函数、ATM/delta 口径、求解器都能换——这些取值官方 README 一个字都没写,全部藏在 util/marketconstants.py 里。这一页把类清单与参数口径列出来,同时明确一件事:本站未实机运行,不给任何定价结果或波动率数值。

3 条曲线 + 曲面 + 2 个定价器拟合默认 CLARK5引擎可切 financepy期权期限 ON–1Y
FXSpotCurve即期曲线
FXForwardsCurve远期曲线 + roll 规则
FXOptionsCurve期权曲线
FXVolSurface波动率曲面(按 strike / delta 取值)
Pricer / VolStatsFXOptionsPricer、FXForwardsPricer、VolStats
curve 包结构示意(依据 finmarketpy 0.11.19 的 curve/curve/rates/curve/volatility/ 源码);示意非官方架构图,不包含任何定价或波动率数值。
FinMarketPy · FinMarketPy · FinMarketPy · FinMarketPy · Classes

curve 包里到底有哪些东西

下表由 finmarketpy 0.11.19 发布包解包实读;括号里是关键方法签名,便于你对照自己安装的版本。

文件职责关键方法适用场景注意点
FXSpotCurvecurve/fxspotcurve.py外汇即期曲线的构建与总收益指数generate_key()get_day_count_conv(currency)构建即期口径的收益序列不同货币的计息天数惯例不同,由 get_day_count_conv 决定
FXForwardsCurvecurve/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 天),改口径会改变长期收益
FXOptionsCurvecurve/fxoptionscurve.py期权曲线与期权总收益指数get_roll_date(horizon_d, expiry_d, asset_hols, month_adj=0)generate_key()期权持仓的连续化与展期期权 roll 事件默认按到期日(expiry-date),与远期的 month-end 不同
FXVolSurfacecurve/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)变体
FXOptionsPricercurve/volatility/fxoptionspricer.py外汇期权定价继承 AbstractPricerget_day_count_conv(currency)对期权做定价定价引擎可选 financepy 或 finmarketpy(见参数口径表)
FXForwardsPricercurve/rates/fxforwardspricer.py外汇远期定价get_forwards_divisor(currency)get_day_count_conv(currency)远期点数与定价点数除数按货币不同(如 JPY 是 100)
VolStatscurve/volatility/volstats.py波动率统计adjust_implied_ON_fri_vol(data_frame)把隐含波动率按周五/隔夜口径调整含「周末效应」的调整逻辑,跨周末数据要留意
AbstractCurve / AbstractPricercurve/abstractcurve.pycurve/abstractpricer.py曲线与定价器的抽象基类fetch_continuous_time_series()construct_total_returns_index()price_instrument()你要自己扩展新品种时的接口约定这两个文件很短,适合作为「怎么接入自有曲线」的入口
Workflow

三条曲线分别解决什么问题

把「即期 → 远期 → 期权」当成三个递进的口径层,选错层会让结论整体偏移。

  1. 先问:你要的是现货涨跌还是持有收益

    只用即期曲线时,收益来自价格变化;一旦引入远期,就要处理展期(roll)——FXForwardsCurve.get_roll_date(horizon_d, delivery_d, asset_hols, month_adj=1) 决定展期发生在哪一天。官方示例注释提醒过:用现货价算收益会漏掉 carry,这是同一件事的另一面。

  2. 远期:把外币资产收益换回本币

    FXForwardsCurve.unhedged_asset_fx(assets_df, asset_currency, home_curr, start_date, finish_date, spot_df=None) 这个方法的用途就是把某一货币资产的收益按未对冲口径换算回本币,适合做「本币视角的组合收益」。参数里要显式给出资产货币与本币。

  3. 期权:把持仓连续化

    期权不能像现货一样长期持有,需要不断展期。FXOptionsCurve 的 roll 事件默认是 expiry-date(到期日),另有 month-end / no-roll 可选;默认交易期限是 1M。也就是说,你看到的「期权总收益指数」是一串展期合约拼起来的。

  4. 曲面:按执行价或按 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() 这类直接取报价点的方法。

  5. 画出来看一眼

    plot_vol_curves() 可以直接把曲面切片画成曲线图;FXVolSurface 还有 get_dom_discount_curve() / get_for_discount_curve() 取两边货币的贴现曲线,方便你核对输入是否符合预期。

关键提醒:曲面拟合的质量高度依赖输入报价的质量与口径。官方的常量里把「按什么规则构造 ATM」「用哪种 delta 定义」都做成了可切换项(见下一节),意味着同一批报价在不同口径下会给出不同的波动率。写研究结论时务必把口径写出来。
Parameters

曲面参数口径有哪些:官方 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_alpha0.5数值参与拟合的权重/插值参数改动会改变曲面形状,属于要记录的实验参数
fx_options_tol1e-8数值求解收敛容差放得太松会静默给出不收敛的结果
fx_options_tenor_for_interpolationON, 1W, 2W, 3W, 1M, 2M, 3M, 4M, 6M, 9M, 1Y列表定义参与插值的期限点源码注释提醒 2Y/3Y 被注释掉了,长端可能不参与
fx_options_roll_event / _roll_days_before / _roll_monthsexpiry-date / 5 / 1month-end / expiry-date / no-roll决定期权怎么展期与远期的 month-end 口径不同,跨曲线比较时要留意
fx_forwards_tenor_for_interpolation1W…5Y列表远期插值期限点比期权多出 2Y/3Y/5Y
fx_forwards_points_divisor_*IDR 为 1、JPY 为 100按货币列表远期点数换算货币写错就会出现数量级错误
fx_forwards_roll_eventmonth-end(提前 5 天)month-end / quarter-end / year-end / delivery-date决定远期展期时点长周期回测里这个口径会显著改变收益
currencies_with_365_basisAUD, CAD, GBP, NZD列表计息基准为 365 的货币基准选错会影响贴现与远期定价
覆盖方式:这些常量都可以被 MarketCred(即 finmarketpy.util.marketcred)覆盖——MarketConstants.__init__ 会逐个比对同名属性;也支持通过 override_fields 字典临时覆盖。研究记录里应写清你改过哪些。
Dependencies

要用这块能力,环境上要额外做什么

曲面与定价不是「装完就能用」——它们牵出一个可选依赖与一类典型报错。

环节官方做法为什么这样做注意点
FinancePy(期权定价)pip install numba numpy scipy llvmlite ipython pandas prettytable,再 pip install financepy==0.370 --no-depsREADME 说明: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 等)曲面质量由报价质量决定报价缺失或口径不一致时,先修数据,别调参数「凑」出平滑曲面
Boundaries

这一块能做什么、不能做什么

把边界说清楚,比给一串公式更有用。

能做:曲面拟合与取值

给定报价与估值日,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。

不能做:给交易建议或价格预测

本站不给任何期权定价结果、波动率数值或交易建议。曲面是研究工具,不是收益承诺;用它做研究时同样要遵守回测可信度体检那一套口径纪律。

关于免部署路线:本机 EasyClaw 的 29 个技能里没有外汇期权、波动率曲面或曲线相关的技能,能对上的只有加密/股票行情取数与出图能力。所以在这一块上,不存在「用技能替代 finmarketpy」的选项——两条路线在这里不重叠,对比页也照此如实说明。
FAQ

外汇曲线与曲面常见问题

类名与常量取值以 0.11.19 源码为准;本站未实机运行,不给出任何定价结果。

不做期权的人,这一块跟回测有关系吗?

有,但主要在远期曲线。FXForwardsCurve 的 roll 规则(默认 month-end、提前 5 天)决定了长周期外汇回测里的展期时点,官方示例也提醒过「用现货价算收益会漏掉 carry」——这正是远期曲线要解决的问题。所以即使你只做现货趋势策略,也建议看一眼 fx_forwards_roll_* 这几个常量。

曲面拟合该选 CLARK5 还是 SABR?

默认值是 CLARK5,另有 CLARKBBGSABRSABR3。不同函数族对微笑两翼的拟合特性不同,适合的报价环境也不同。本站不给推荐:换函数族必须重做口径记录并重跑对照——因为同一个 delta 点在不同函数族下会给出不同的波动率。以官方实现与你的实证结果为准。

ATM 和 delta 为什么各有四种口径?

因为外汇场外市场的报价惯例本身有多种版本:ATM 可以是 fwd-delta-neutralfwd-delta-neutral-premium-adjspotfwd;delta 可以是 fwd-deltafwd-delta-prem-adjspot-deltaspot-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 + 自备报价数据这一条路。

下一步看哪些能力:宏观事件与季节性

如果说 curve/ 面向的是定价研究,那 economics/ 面向的就是「某类事件发生时市场会怎样」这一类问题。下一页整理这块的方法结构与数据来源。