Skip to article frontmatterSkip to article content
FreqTrade 高级超参数优化指南

高级超参数优化

深入了解超参数优化功能

高级超参数优化(Hyperopt)

本页介绍一些高级 Hyperopt 主题,可能比创建普通超参数优化类需要更高的编码技能和 Python 知识。

创建和使用自定义损失函数

要使用自定义损失函数类,请确保你的自定义 hyperopt 损失类中定义了 hyperopt_loss_function 函数。 对于下方的示例,你需要在 hyperopt 命令中添加参数 --hyperopt-loss SuperDuperHyperOptLoss,以便使用该函数。

下面是一个示例,与默认 Hyperopt 损失实现完全相同。完整示例可见于 userdata/hyperopts

from datetime import datetime
from typing import Any, Dict

from pandas import DataFrame

from freqtrade.constants import Config
from freqtrade.optimize.hyperopt import IHyperOptLoss

TARGET_TRADES = 600
EXPECTED_MAX_PROFIT = 3.0
MAX_ACCEPTED_TRADE_DURATION = 300

class SuperDuperHyperOptLoss(IHyperOptLoss):
    """
    定义 hyperopt 的默认损失函数
    """

    @staticmethod
    def hyperopt_loss_function(
        *,
        results: DataFrame,
        trade_count: int,
        min_date: datetime,
        max_date: datetime,
        config: Config,
        processed: dict[str, DataFrame],
        backtest_stats: dict[str, Any],
        starting_balance: float,
        **kwargs,
    ) -> float:
        """
        目标函数,结果越小越好
        这是传统算法(freqtrade 一直使用的)。权重分配如下:
        * 0.4 给交易时长
        * 0.25:避免交易亏损
        * 1.0 给总利润,相对于上面定义的期望值(`EXPECTED_MAX_PROFIT`)
        """
        total_profit = results['profit_ratio'].sum()
        trade_duration = results['trade_duration'].mean()

        trade_loss = 1 - 0.25 * exp(-(trade_count - TARGET_TRADES) ** 2 / 10 ** 5.8)
        profit_loss = max(0, 1 - total_profit / EXPECTED_MAX_PROFIT)
        duration_loss = 0.4 * min(trade_duration / MAX_ACCEPTED_TRADE_DURATION, 1)
        result = trade_loss + profit_loss + duration_loss
        return result

目前,参数说明如下:

该函数需返回一个浮点数(float)。数值越小,结果越好。参数和权重分配可自行调整。

覆盖预定义空间

要覆盖预定义空间(如 roi_spacegenerate_roi_tablestoploss_spacetrailing_spacemax_open_trades_space),请在策略中定义一个嵌套的 Hyperopt 类,并如下定义所需空间:

from freqtrade.optimize.space import Categorical, Dimension, Integer, SKDecimal

class MyAwesomeStrategy(IStrategy):
    class HyperOpt:
        # 自定义止损空间
        def stoploss_space():
            return [SKDecimal(-0.05, -0.01, decimals=3, name='stoploss')]

        # 自定义 ROI 空间
        def roi_space() -> List[Dimension]:
            return [
                Integer(10, 120, name='roi_t1'),
                Integer(10, 60, name='roi_t2'),
                Integer(10, 40, name='roi_t3'),
                SKDecimal(0.01, 0.04, decimals=3, name='roi_p1'),
                SKDecimal(0.01, 0.07, decimals=3, name='roi_p2'),
                SKDecimal(0.01, 0.20, decimals=3, name='roi_p3'),
            ]

        def generate_roi_table(params: Dict) -> dict[int, float]:

            roi_table = {}
            roi_table[0] = params['roi_p1'] + params['roi_p2'] + params['roi_p3']
            roi_table[params['roi_t3']] = params['roi_p1'] + params['roi_p2']
            roi_table[params['roi_t3'] + params['roi_t2']] = params['roi_p1']
            roi_table[params['roi_t3'] + params['roi_t2'] + params['roi_t1']] = 0

            return roi_table

        def trailing_space() -> List[Dimension]:
            # 这里所有参数都是必需的,只能修改类型或范围。
            return [
                # 固定为 true,如果优化 trailing_stop,则假定始终使用追踪止损。
                Categorical([True], name='trailing_stop'),

                SKDecimal(0.01, 0.35, decimals=3, name='trailing_stop_positive'),
                # 'trailing_stop_positive_offset' 应大于 'trailing_stop_positive',
                # 所以这里用中间参数表示两者的差值。'trailing_stop_positive_offset' 的值在
                # generate_trailing_params() 方法中构造。
                # 这类似于用于构造 ROI 表的 hyperspace 维度。
                SKDecimal(0.001, 0.1, decimals=3, name='trailing_stop_positive_offset_p1'),

                Categorical([True, False], name='trailing_only_offset_is_reached'),
        ]

        # 自定义最大持仓数空间
        def max_open_trades_space(self) -> List[Dimension]:
            return [
                Integer(-1, 10, name='max_open_trades'),
            ]

动态参数

参数也可以动态定义,但在 bot_start() 回调 被调用后,实例必须能访问到这些参数。


class MyAwesomeStrategy(IStrategy):

    def bot_start(self, **kwargs) -> None:
        self.buy_adx = IntParameter(20, 30, default=30, optimize=True)

    # ...

覆盖 Base estimator

你可以通过在 Hyperopt 子类中实现 generate_estimator(),为 Hyperopt 定义自己的 optuna 采样器。

class MyAwesomeStrategy(IStrategy):
    class HyperOpt:
        def generate_estimator(dimensions: List['Dimension'], **kwargs):
            return "NSGAIIISampler"

可用值包括:

详情见 optuna-samplers 文档,也可以是继承自 optuna.samplers.BaseSampler 的类实例。

有时需要自行研究以发现更多采样器(如 optunahub 提供的)。

空间类型选项

对于附加空间,scikit-optimize(结合 Freqtrade)提供以下空间类型:

你可以从 freqtrade.optimize.space 导入所有这些类型,尽管 CategoricalIntegerReal 只是 scikit-optimize 空间的别名。SKDecimal 由 freqtrade 提供,用于更快的优化。

from freqtrade.optimize.space import Categorical, Dimension, Integer, SKDecimal, Real  # noqa