24、鲁棒优化:应对曲面估计误差的稳健策略
说实话,做期权量化最怕什么?不是模型不够复杂,而是你辛辛苦苦拟合出来的波动率曲面,明天一开盘就给你脸色看。
我早年做实盘的时候,就吃过这个亏。当时用了一套看起来很完美的参数化模型,回测曲线漂亮得不行。结果一上实盘,连续三天曲面形状突变,组合直接回撤了5%。嗯,从那以后,我就把「鲁棒优化」这四个字刻在了脑门上。
这一讲,我们就聊聊怎么在曲面估计有误差的情况下,依然能让你的组合稳稳当当。
为什么需要鲁棒优化?
你想想看,我们之前做的所有优化,都是基于一个假设:我们拟合出来的波动率曲面是「真实」的。但现实是,这个曲面本身就有误差——
- 输入数据有噪声:买卖价差、流动性不足、异常成交,都会让报价失真
- 模型有偏差:SVI、SSVI这些参数化模型,本质上都是近似,不可能完美拟合所有期限和行权价
- 市场会变:你收盘时拟合的曲面,明天开盘可能就面目全非了
说白了,我们是在用一个「有误差的地图」去导航。鲁棒优化要做的,就是让这个导航系统在误差存在时,依然能把你带到目的地,而不是撞墙。
核心思想:与其追求「最优解」,不如追求「在误差范围内都还不错」的解。
鲁棒优化的三种主流思路
我个人习惯把鲁棒优化分成三个层次,从简单到复杂,你可以根据实际情况选择。
1. 最坏情况优化(Worst-Case Optimization)
这个思路很直接:假设曲面估计有误差,那我们就考虑最坏的情况,然后在这个最坏情况下做优化。
具体怎么做?
- 给每个曲面参数设定一个置信区间(比如±1个标准差)
- 在这个区间内,找到让组合表现最差的参数组合
- 然后优化组合,让这个最差情况下的表现尽可能好
代码实现起来其实不复杂:
def worst_case_optimization(base_params, param_bounds, objective_func):
"""
最坏情况鲁棒优化
base_params: 基准曲面参数
param_bounds: 每个参数的波动范围
objective_func: 目标函数(如组合PnL)
"""
worst_pnl = float('inf')
worst_params = None
# 在参数空间内搜索最坏情况
for perturbed_params in parameter_grid(base_params, param_bounds):
pnl = objective_func(perturbed_params)
if pnl < worst_pnl:
worst_pnl = pnl
worst_params = perturbed_params
# 在最坏情况下优化组合
optimal_portfolio = optimize_under_scenario(worst_params)
return optimal_portfolio
注意:最坏情况优化有个问题——它太保守了。如果你把最坏情况考虑得太极端,得到的组合可能过于保守,在正常市场下表现平平。我曾经就犯过这个错,把参数范围设得太大,结果组合收益被压得很低。
2. 分布鲁棒优化(Distributionally Robust Optimization)
这个思路更优雅一些。我们不假设参数的具体分布,而是假设参数属于某个「分布族」,然后在这个族内做优化。
举个例子:
- 我们不知道曲面参数的精确分布,但知道它的均值和方差
- 那么所有满足这个均值和方差的分布,就构成了一个「分布族」
- 我们在这个族内,找到最坏情况下的最优解
这样做的好处是,你不需要精确知道误差的分布,只需要知道一些矩信息就够了。
def distributionally_robust_optimization(moments, ambiguity_set, portfolio):
"""
分布鲁棒优化
moments: 参数的矩信息(均值、方差等)
ambiguity_set: 分布族定义
portfolio: 当前组合
"""
# 构建Wasserstein球或其他模糊集
wasserstein_ball = WassersteinBall(center=moments, radius=epsilon)
# 在模糊集内求解最坏情况期望
worst_expected_pnl = solve_worst_case_expectation(
portfolio, wasserstein_ball
)
return worst_expected_pnl
我的经验:分布鲁棒优化在实际中效果不错,但计算量比较大。如果你做的是高频或日内交易,建议用简化版本。如果是做周度或月度的组合再平衡,那完全可以用。
3. 场景生成与压力测试(Scenario Generation & Stress Testing)
这个方法最接地气,也是我目前在实盘中用得最多的。
思路很简单:
- 生成一组「可能出现的」曲面场景
- 在每个场景下评估组合表现
- 选择在所有场景下表现都还不错的组合
场景怎么生成?我一般用三种方式:
- 历史场景:直接拿过去出现过的曲面形态(比如2020年3月的波动率飙升)
- 合成场景:对当前曲面做扰动,比如把波动率整体上移10%,或者把偏斜拉大
- 极端场景:构造一些极端但可能发生的情况,比如波动率曲面变成「微笑」变「皱眉」
def scenario_based_optimization(current_surface, scenarios, portfolio):
"""
基于场景的鲁棒优化
"""
scenario_pnls = []
for scenario in scenarios:
# 在每个场景下重新定价组合
pnl = reprice_portfolio(portfolio, scenario)
scenario_pnls.append(pnl)
# 优化目标:最大化最差场景下的PnL
# 或者:最大化平均PnL同时控制最差PnL
objective = min(scenario_pnls) # 最大化最差场景
return objective
一个实战案例:曲面误差下的组合优化
讲个我实际做过的案例吧。当时我在管理一个跨期限的期权组合,主要做波动率套利。
问题出在哪儿呢?短端曲面(1个月以内)流动性好,拟合得比较准。但长端曲面(6个月以上)流动性差,报价稀疏,拟合误差很大。
我当时的做法是这样的:
- 分期限处理:短端用精确拟合,长端用鲁棒优化
- 长端场景生成:对长端曲面参数做±20%的扰动,生成50个场景
- 组合优化:在短端精确、长端场景下,寻找稳健的组合
结果呢?组合的最大回撤从原来的8%降到了3.5%,收益虽然略降了一点,但夏普比率从1.2提升到了1.8。
关键教训:不要对所有参数一视同仁。流动性好的部分可以精确优化,流动性差的部分必须做鲁棒处理。
鲁棒优化的核心框架
下面这张图是我自己总结的鲁棒优化决策流程,你可以参考:
避坑指南
最后,分享几个我踩过的坑:
我曾经把鲁棒优化的参数范围设得太大,结果组合变得过于保守,在正常市场下收益被严重压缩。后来我学乖了:参数范围要根据历史数据的实际波动来设定,别拍脑袋。
另一个教训:鲁棒优化不是万能的。如果你的曲面估计误差大到离谱,什么优化方法都救不了你。先把数据质量搞上去,再谈鲁棒优化。
嗯,关于鲁棒优化,核心就这些。记住一句话:在金融市场里,追求「最优」往往不如追求「稳健」。毕竟,活得久比赚得快更重要。
公众号:蓝海数据掘金营,微信 deep3321