16、多目标优化:多目标优化问题定义、Pareto前沿、加权求和法、NSGA-II算法、多目标调优实践

做基差交易策略调优,最头疼的是什么?

不是参数太多,而是你发现——收益和回撤,天生就是一对冤家

你调高杠杆,收益上去了,回撤也跟着飙升。你收紧止损,回撤控制住了,收益又缩水了。这就是典型的多目标优化问题。今天我们就来聊聊,怎么在多个互相打架的目标之间,找到那个最舒服的平衡点。

16.1 多目标优化问题定义

先给个定义。多目标优化,说白了就是同时优化多个目标函数。在基差交易里,常见的优化目标有:

  • 最大化年化收益率
  • 最小化最大回撤
  • 最大化夏普比率
  • 最小化交易成本

这些目标之间,往往是冲突的。你不可能让所有目标都达到最优。这就是多目标优化的核心难点——没有绝对的最优解,只有折中解

数学定义:

给定决策变量 x,优化目标函数 f₁(x), f₂(x), ..., fₙ(x),在约束条件下,找到一组解,使得所有目标函数尽可能好。

我在项目中遇到过最典型的例子:一个螺纹钢跨期套利策略,参数调优时,年化收益从12%提到18%,但最大回撤从3%飙到了8%。你说哪个好?这得看你的风险承受能力。所以,多目标优化的本质,是帮你在帕累托前沿上做选择。

16.2 Pareto前沿

Pareto前沿,也叫帕累托最优边界。这个概念其实不复杂。

假设有两个解 A 和 B。如果 A 在所有目标上都优于或等于 B,并且至少有一个目标严格优于 B,那我们就说 A 支配 B。所有不被其他解支配的解,就构成了Pareto前沿

举个例子。你调优一个豆粕基差策略,得到三组参数:

参数组 年化收益 最大回撤
方案A 15% 4%
方案B 18% 6%
方案C 12% 3%

你看,A、B、C 之间互不支配。A收益比C高,但回撤也比C大。B收益最高,但回撤也最大。这三个方案都在Pareto前沿上。你选哪个?取决于你的偏好。

我的经验:在实际调优中,我一般会先跑出Pareto前沿,然后根据资金曲线和实盘感受,选一个自己最舒服的点。别迷信数学最优,交易是门艺术。

16.3 加权求和法

加权求和法是最直观的多目标处理方法。把多个目标加权成一个综合指标:

综合得分 = w₁ * 收益 - w₂ * 回撤 + w₃ * 夏普比率

权重 w₁、w₂、w₃ 由你设定。比如你特别在意回撤,就把 w₂ 设大一点。

听起来很简单对吧?但这里有个坑——目标量纲不同。收益是百分比,回撤也是百分比,但夏普比率是比值。直接加权,权重意义不明确。

我曾经踩过的坑:一开始直接用原始值加权,结果夏普比率权重设了0.3,收益权重设了0.5,但夏普比率数值小,几乎没起作用。后来我改成先归一化,再加权,效果就好多了。

归一化方法:

归一化值 = (原始值 - 最小值) / (最大值 - 最小值)

这样所有目标都在0到1之间,权重才有实际意义。

16.4 NSGA-II算法

加权求和法有个问题——你只能得到一个解。如果你想看整个Pareto前沿,就得反复调权重,效率很低。这时候,NSGA-II就派上用场了。

NSGA-II 是「非支配排序遗传算法」的第二版。它一次运行,就能找到一组分布在Pareto前沿上的解。核心思想就两步:

  1. 非支配排序:把种群分成不同层级,Pareto前沿上的解排第一层,被支配的解排后面。
  2. 拥挤度计算:在同一层级里,优先保留那些分布稀疏的解,保证解的多样性。

说白了,NSGA-II 就是在「找好解」和「找不同的好解」之间做平衡。我习惯用 Python 的 pymoo 库来实现:

from pymoo.algorithms.moo.nsga2 import NSGA2
from pymoo.optimize import minimize
from pymoo.core.problem import ElementwiseProblem

class MyProblem(ElementwiseProblem):
    def __init__(self):
        super().__init__(n_var=3, n_obj=2, xl=[0,0,0], xu=[1,1,1])
    
    def _evaluate(self, x, out, *args, **kwargs):
        # 假设三个参数:开仓阈值、止损比例、仓位比例
        threshold, stop_loss, position = x
        # 这里调用你的回测函数
        ret, drawdown = backtest(threshold, stop_loss, position)
        out["F"] = [-ret, drawdown]  # 收益取负,因为pymoo默认最小化

algorithm = NSGA2(pop_size=100)
res = minimize(MyProblem(), algorithm, termination=('n_gen', 50))

跑完之后,res.F 就是Pareto前沿上的所有解。你可以画个散点图,一目了然。

注意:NSGA-II 是启发式算法,每次运行结果可能略有不同。建议多跑几次,取稳定解。

16.5 多目标调优实践

理论说完了,来点实战。我拿一个真实的铁矿石基差套利策略举例。

策略参数有三个:

  • 开仓阈值:基差偏离多少时开仓(0.5% ~ 2.0%)
  • 止损比例:亏损多少止损(0.5% ~ 3.0%)
  • 持仓周期:最长持有天数(5 ~ 20天)

优化目标两个:最大化年化收益最小化最大回撤

我用了NSGA-II,种群大小200,迭代100代。结果如下:

方案 开仓阈值 止损比例 持仓周期 年化收益 最大回撤
P1 1.2% 1.5% 12天 16.3% 4.1%
P2 0.8% 2.0% 15天 19.7% 6.8%
P3 1.5% 1.0% 8天 13.2% 2.9%

这三个方案都在Pareto前沿上。我个人习惯选P1——收益不错,回撤可控。P2收益高但回撤大,适合激进型。P3回撤小但收益低,适合保守型。

避坑指南:我曾经直接选了P2,结果实盘遇到一次极端行情,回撤直接超了历史最大值。从那以后,我选参数都会留点安全边际,比如在Pareto前沿上选一个回撤比预期小20%的点。

最后,多目标优化不是一劳永逸的。市场在变,参数也要跟着调。我建议每季度重新跑一次NSGA-II,看看Pareto前沿有没有漂移。

嗯,多目标优化就聊到这儿。记住一句话:没有完美的策略,只有适合你的折中


公众号:蓝海资料掘金营,微信deep3321