4、奖励函数工程:夏普比率作为奖励信号,回撤惩罚项的设计,多目标奖励的加权与归一化

奖励函数,说白了就是交易智能体的「指挥棒」。你给它什么信号,它就往哪个方向跑。我见过太多人把强化学习当成黑盒子,随便写个收益率当奖励就往上怼,结果模型要么疯狂赌博,要么直接躺平。嗯,这里面的坑,我踩过不少。

今天咱们就聊聊怎么设计一套靠谱的奖励函数。核心就三件事:用夏普比率当主信号,把回撤惩罚加进去,最后搞定多目标加权和归一化。

核心观点:奖励函数不是越复杂越好,而是越「对齐目标」越好。你的交易目标是稳健盈利,那奖励函数就得同时反映收益和风险。

4.1 夏普比率作为奖励信号

为什么选夏普比率?因为它同时考虑了收益和波动。我个人习惯把夏普比率作为奖励函数的主干,而不是直接用收益率。

简单来说,夏普比率 = (收益率 - 无风险利率) / 波动率。但在强化学习里,我们通常用简化版本:

# 简化版夏普比率计算
def sharpe_ratio(rewards):
    # rewards: 一段时间内的收益率序列
    mean_reward = np.mean(rewards)
    std_reward = np.std(rewards)
    if std_reward == 0:
        return 0
    return mean_reward / std_reward * np.sqrt(252)  # 年化

我在项目中遇到过一个问题:直接用整个回测期的夏普比率做奖励,模型训练特别慢。为什么?因为每个step都要等整个episode结束才能算奖励,这延迟太大了。

后来我改用「滚动夏普比率」——用过去N步的收益率窗口来计算。这样每个step都能拿到一个实时的奖励信号:

def rolling_sharpe(reward_history, window=20):
    if len(reward_history) < window:
        return 0
    window_rewards = reward_history[-window:]
    return np.mean(window_rewards) / (np.std(window_rewards) + 1e-8)

小技巧:窗口大小选20-60步比较合适。太短了噪声大,太长了反应慢。我一般用30步,相当于1.5个交易日的分钟级数据。

4.2 回撤惩罚项的设计

光有夏普比率还不够。你想想看,一个策略可能夏普比率很高,但中间回撤了50%,你受得了吗?反正我受不了。

回撤惩罚,就是让智能体学会「别把净值曲线搞得太难看」。我常用的方法有两种:

  1. 最大回撤惩罚:当回撤超过阈值时,给一个大的负奖励
  2. 连续回撤惩罚:每次回撤都扣分,回撤越深扣得越狠
def drawdown_penalty(equity_curve, threshold=0.1):
    # equity_curve: 净值曲线
    peak = np.maximum.accumulate(equity_curve)
    drawdown = (peak - equity_curve) / peak
    
    # 超过阈值的部分加倍惩罚
    penalty = np.where(drawdown > threshold, 
                      drawdown * 2,  # 超阈值加倍
                      drawdown * 0.5)  # 正常惩罚
    
    return -np.mean(penalty)  # 返回负值作为惩罚

避坑指南:我曾经把回撤惩罚设得太重,结果模型变得极度保守,几乎不交易。后来我把惩罚系数调到了0.1-0.3之间,才找到平衡点。记住,惩罚的目的是「约束」,不是「禁止」。

4.3 多目标奖励的加权与归一化

实际交易中,我们往往有多个目标:既要收益高,又要回撤小,还要交易次数合理。这就涉及到多目标奖励的加权组合。

我常用的奖励函数结构是这样的:

def composite_reward(returns, equity_curve, trade_count):
    # 1. 夏普比率信号
    sharpe = rolling_sharpe(returns, window=30)
    
    # 2. 回撤惩罚
    dd_penalty = drawdown_penalty(equity_curve, threshold=0.1)
    
    # 3. 交易频率惩罚(防止过度交易)
    freq_penalty = -0.01 * trade_count if trade_count > 10 else 0
    
    # 4. 加权组合
    reward = (0.6 * sharpe + 
              0.3 * dd_penalty + 
              0.1 * freq_penalty)
    
    return reward

这里有个关键问题:不同目标的量纲不一样。夏普比率可能是0.5-2.0,回撤惩罚可能是-0.1到0,交易频率惩罚可能是-0.5到0。直接加权的话,量纲大的会主导训练。

所以归一化很重要。我一般用两种方法:

方法 公式 适用场景
Min-Max归一化 (x - min) / (max - min) 已知上下界的目标
Z-score归一化 (x - mean) / std 分布稳定的目标

我的习惯:先用Z-score把各个奖励分量归一化到均值为0、标准差为1的分布,然后再加权。这样权重才能真正反映「重要性」,而不是被量纲绑架。

4.4 整体框架图

下面这张图展示了奖励函数工程的核心流程。从原始交易数据到最终的复合奖励,每一步都有讲究:

奖励函数工程核心流程 原始交易数据 夏普比率计算 回撤惩罚计算 交易频率惩罚 Z-score归一化 Z-score归一化 Z-score归一化 加权求和 (0.6 + 0.3 + 0.1) 复合奖励信号

4.5 实战中的注意事项

最后分享几个实战经验:

  • 奖励不要设计得太「密」:每个step都给奖励,模型容易过拟合短期噪声。我习惯每5-10步给一次奖励,或者用累积收益来算。
  • 权重需要调参:0.6/0.3/0.1不是金科玉律。我一般先跑一组实验,看各个分量的量级,再调整权重让它们贡献相当。
  • 记得加一个「生存奖励」:如果模型一直不交易,给它一个小的负奖励,鼓励它至少尝试一下。不然模型可能学会「躺平」。

总结一下:奖励函数工程,本质上是在告诉智能体「什么是对的」。夏普比率告诉它要稳健,回撤惩罚告诉它别玩火,多目标加权告诉它怎么平衡。这三板斧用好了,你的交易智能体至少不会跑偏。

专注资料整理