第 7 章 · 03 损失函数


文档摘要

第 7 章 · 03 损失函数 本节摘要:本节讲清 Hyperopt 的「方向盘」——损失函数(loss function)。损失函数定义了「什么叫好」,Hyperopt 会朝它指向的方向搜索。freqtrade 内置十余种损失函数,它们各有侧重: 只看利润、 优化风险调整后收益、 压回撤、 多指标综合。选错损失函数,Hyperopt 可能把策略推向「高利润但巨回撤」或「稳但不赚」的极端。本节先讲损失函数在 Hyperopt 闭环里的角色,再用一张表对比所有内置损失函数的侧重和适用场景,给出选择决策树;然后挑两个代表性实现( 和 )看它们的代码逻辑;最后讲怎么用 写自定义损失函数,以及「损失函数越小越好」的方向约定。

第 7 章 · 03 损失函数

本节摘要:本节讲清 Hyperopt 的「方向盘」——损失函数(loss function)。损失函数定义了「什么叫好」,Hyperopt 会朝它指向的方向搜索。freqtrade 内置十余种损失函数,它们各有侧重:OnlyProfitHyperOptLoss 只看利润、SharpeHyperOptLoss 优化风险调整后收益、MaxDrawDownHyperOptLoss 压回撤、MultiMetricHyperOptLoss 多指标综合。选错损失函数,Hyperopt 可能把策略推向「高利润但巨回撤」或「稳但不赚」的极端。本节先讲损失函数在 Hyperopt 闭环里的角色,再用一张表对比所有内置损失函数的侧重和适用场景,给出选择决策树;然后挑两个代表性实现(SharpeHyperOptLossMultiMetricHyperOptLoss)看它们的代码逻辑;最后讲怎么用 IHyperOptLoss 写自定义损失函数,以及「损失函数越小越好」的方向约定。

内容来源:原项目文档 docs/hyperopt.md(Loss functions)、docs/advanced-hyperopt.md(Custom loss)、源码 freqtrade/optimize/hyperopt_loss/,汉化并套用体系化模板。

学习目标

阅读完本节,你应当能够:

  1. 解释损失函数在 Hyperopt 闭环里的角色(定义「好」的方向)。
  2. 列举主要内置损失函数及其侧重。
  3. 根据策略类型选对损失函数(保守 vs 激进、单币 vs 多币)。
  4. 懂代表性损失函数的源码逻辑(Sharpe、MultiMetric)。
  5. IHyperOptLoss 写一个自定义损失函数。

一、损失函数的角色

回顾第 01 节的 Hyperopt 闭环:

损失函数是闭环里的评分器:它把一次回测的所有结果(交易列表、统计、起止余额等)压缩成一个标量 float——这个数越小,代表结果越好。Optuna 采样器根据这个分数决定下一组参数往哪探索。

💡 方向约定:所有损失函数都遵循「返回值越小越好」。所以像 Sharpe 这种「越大越好」的指标,损失函数会返回 -sharpe(取负),让它变成「越小(越负)= Sharpe 越大 = 越好」。

损失函数通过 --hyperopt-loss <类名> 指定:

freqtrade hyperopt --hyperopt-loss SharpeHyperOptLossDaily --strategy MyStrategy -e 500

或写进配置:

{"hyperopt_loss": "SharpeHyperOptLossDaily"}

二、内置损失函数速查

freqtrade 在 freqtrade/optimize/hyperopt_loss/ 下内置了 13 种损失函数。按「侧重维度」分类:

利润类

类名 侧重 适用
OnlyProfitHyperOptLoss 只看总利润 只关心赚多少,不管波动
ShortTradeDurHyperOptLoss 短持仓 + 避亏(旧默认) 偏好快进快出的短线

风险调整类(推荐)

类名 侧重 适用
SharpeHyperOptLoss Sharpe 比率(收益/总波动) 想要「平滑上升」的资金曲线
SharpeHyperOptLossDaily 日频 Sharpe(推荐) 比按交易更稳定,抑制单笔异常
SortinoHyperOptLoss Sortino(只算下行波动) 不惩罚上涨波动
SortinoHyperOptLossDaily 日频 Sortino 同上,日频更稳
CalmarHyperOptLoss Calmar(年化收益/最大回撤) 同时关注收益与回撤

回撤类

类名 侧重 适用
MaxDrawDownHyperOptLoss 最小化最大绝对回撤 极度厌恶回撤
MaxDrawDownRelativeHyperOptLoss 绝对 + 相对回撤兼顾 平衡型
MaxDrawDownPerPairHyperOptLoss 按交易对算盈亏/回撤,取最差 多币策略,防某币拖后腿
ProfitDrawDownHyperOptLoss 最大利润 + 最小回撤 平衡型,DRAWDOWN_MULT 可调

综合类

类名 侧重 适用
MultiMetricHyperOptLoss 多指标综合(利润+回撤+PF+期望+胜率+交易数) 新手默认推荐,平衡不易走极端

三、怎么选:决策树

经验法则

  • 新手起步:MultiMetricHyperOptLoss。它综合多维度,最不易把策略推向极端。
  • 想要稳定资金曲线:SharpeHyperOptLossDaily(官方 quick-start 示例就用它)。
  • 多币策略:MaxDrawDownPerPairHyperOptLoss,防止一两个好币掩盖其他币的亏损。
  • 慎用 OnlyProfitHyperOptLoss:它只看利润,容易优化出「高利润但巨回撤、靠一两次极端行情」的策略,实盘心理难承受。

💡 可以多跑几次对比:同一策略,分别用 Sharpe、Sortino、MultiMetric 各跑一轮,看找出的参数是否一致。如果不同损失函数都收敛到相近参数,说明策略稳健;如果差异巨大,说明它在某个维度上是「勉强凑出来的」。

四、看源码:SharpeHyperOptLoss

最经典的风险调整损失,源码极简(freqtrade/optimize/hyperopt_loss/hyperopt_loss_sharpe.py):

class SharpeHyperOptLoss(IHyperOptLoss): @staticmethod def hyperopt_loss_function(results, min_date, max_date, starting_balance, *args, **kwargs): sharp_ratio = calculate_sharpe(results, min_date, max_date, starting_balance) return -sharp_ratio

逻辑就一句话:算 Sharpe,取负返回。Sharpe = (平均收益 - 无风险收益) / 收益标准差,衡量「每单位波动换来的收益」。-sharp_ratio 让「Sharpe 越大 → 返回值越小 → Hyperopt 觉得越好」。

SharpeHyperOptLossDaily 是它的日频版本:用每日收益而非每笔交易收益算 Sharpe,对「单笔异常巨利」不敏感,更稳健。

五、看源码:MultiMetricHyperOptLoss

新手推荐的综合性损失(hyperopt_loss_multi_metric.py),它综合了 6 个维度:

# 简化版逻辑 total_profit = results["profit_abs"].sum() profit_factor = winning_profit / (abs(losing_profit) + 1e-6) _, expectancy_ratio = calculate_expectancy(results) winrate = len(winning_trades) / len(results) relative_account_drawdown = calculate_max_drawdown(...)["relative_account_drawdown"] # 交易数过少则惩罚 trade_count_penalty = 1.0 if trade_count >= 50 else max(0.1, 1 - ...) # 综合:利润 - 回撤折扣,再乘以各因子的对数 profit_draw_function = total_profit - (relative_account_drawdown * total_profit) * (1 - DRAWDOWN_MULT) return -1 * (profit_draw_function * log(profit_factor + PF_CONST) * log(min(10, expectancy_ratio) + EXPECTANCY_CONST) * log(WINRATE_CONST + winrate) * trade_count_penalty)

设计要点:

  1. 乘法结构:利润×PF×期望×胜率,任何一项差都会拉低总分,防止「靠一个维度刷分」。
  2. 对数平滑:log(... + const) 避免某个因子过大主导,也让边际收益递减。
  3. 回撤折扣:从利润里扣掉「回撤占利润的比例」,回撤越大扣越多。
  4. 交易数惩罚:少于 50 笔交易就打折,防止「3 笔全胜」这种小样本侥幸。

💡 为什么推荐 MultiMetric:它的乘法结构天然抑制极端——只刷利润不管回撤的策略,会被回撤折扣拉低;只刷胜率但单笔小的,会被 expectancy 拉低。结果更「全面发展」。

六、MaxDrawDownPerPair:多币策略的护身符

源码逻辑(hyperopt_loss_max_drawdown_per_pair.py):

score_per_pair = [] for p in backtest_stats["results_per_pair"]: if p["key"] != "TOTAL": profit = p.get("profit_total_abs", 0) drawdown = p.get("max_drawdown_abs", 0) profit_dd = profit / drawdown if drawdown != 0 and profit != 0 else profit score = profit_dd - penalty if profit_dd < 1.0 else profit_dd score_per_pair.append(score) return -min(score_per_pair) # 取最差的那个币的分数

关键在最后一行:-min(score_per_pair)——它取所有交易对里表现最差的那个作为整体分数。这样 Hyperopt 被迫同时优化所有币,不能靠一两个好币掩盖其他币的亏损。多币策略强烈推荐。

七、写自定义损失函数

内置的不够用?写一个。两步:

步骤 1:写类

user_data/hyperopts/ 下新建文件(如 my_loss.py),继承 IHyperOptLoss,实现 hyperopt_loss_function:

from datetime import datetime from pandas import DataFrame from freqtrade.constants import Config from freqtrade.optimize.hyperopt import IHyperOptLoss class MyCustomLoss(IHyperOptLoss): @staticmethod def hyperopt_loss_function( *, results: DataFrame, # 交易 DataFrame trade_count: int, # = len(results) min_date: datetime, max_date: datetime, config: Config, processed: dict, # {pair: DataFrame} backtest_stats: dict, # 回测统计 starting_balance: float, **kwargs, ) -> float: # 你的打分逻辑:越小越好 total_profit = results['profit_ratio'].sum() winrate = (results['profit_ratio'] > 0).mean() # 例:利润 * 胜率,取负 return -(total_profit * winrate)

可用的参数(results DataFrame 的列):

pair, profit_ratio, profit_abs, open_date, open_rate, fee_open, close_date, close_rate, fee_close, amount, trade_duration, is_open, exit_reason, stake_amount, min_rate, max_rate, stop_loss_ratio, stop_loss_abs

backtest_statsresults_per_pair 等结构(见 optimize_reports.pygenerate_strategy_stats())。

步骤 2:使用

freqtrade hyperopt --hyperopt-loss MyCustomLoss --strategy MyStrategy -e 500

写自定义损失的注意点

  • 保持 *args, **kwargs:接口未来可能扩展,留着兼容。
  • 返回 float:必须是标量,越小越好。
  • 性能优先:这个函数每个 epoch 调用一次,别在里面做重计算。
  • 加惩罚防过拟合:可以加「交易数过少惩罚」「回撤过大惩罚」等,让结果更稳健(参考 MultiMetric)。

⚠️ 方向别写反:记住「返回值越小越好」。如果你用「越大越好」的指标(如利润、Sharpe、胜率),记得取负或用 1 - x 之类变换。

本节要点回顾

  1. 角色:损失函数是 Hyperopt 的方向盘,把回测结果压成一个 float,越小越好;通过 --hyperopt-loss 指定。
  2. 内置 13 种:利润类(OnlyProfit、ShortTradeDur)、风险调整类(Sharpe/Sortino/Calmar,各有日频版)、回撤类(MaxDrawDown 系列)、综合类(MultiMetric)。
  3. 选择决策:新手用 MultiMetric;求稳用 SharpeHyperOptLossDaily;多币用 MaxDrawDownPerPairHyperOptLoss;慎用 OnlyProfit。
  4. Sharpe 源码:极简,return -calculate_sharpe(...),取负让大 Sharpe = 小损失。
  5. MultiMetric 设计:乘法结构(利润×PF×期望×胜率)+ 回撤折扣 + 交易数惩罚,抑制极端、全面发展。
  6. MaxDrawDownPerPair:-min(score_per_pair) 取最差币,强制全币优化,多币护身符。
  7. 自定义:继承 IHyperOptLoss,实现 hyperopt_loss_function,放 user_data/hyperopts/,保持 **kwargs,返回 float,方向别写反。

下一节(第 7 章末节),我们讲高级优化——随机 vs TPE 等采样器选择、产出 JSON 怎么导入策略、以及避免过拟合的实战清单。


发布者: 作者: 灏天文库 转发
评论区 (0)
U