Skip to content

Hyperopt

本页面介绍如何通过寻找最优参数来调整策略,这个过程称为超参数优化。机器人使用scikit-optimize包中的算法来完成这个过程。搜索会使用所有的CPU核心,让你的笔记本声音像战斗机,但仍然需要很长时间。

一般来说,搜索最佳参数从几个随机组合开始(有关详细信息,请参见下文),然后使用贝叶斯搜索和机器学习回归算法(当前为ExtraTreesRegressor)快速找到搜索超空间中参数组合,使损失函数的值最小化。

Hyperopt需要有历史数据可用,就像回测一样(hyperopt会使用不同的参数进行多次回测)。 要了解如何获取您感兴趣的交易对和交易所的数据,请前往文档中的数据下载部分。

Bug

Hyperopt在只使用1个CPU核心时可能会崩溃,详见Issue #1133

Note

自从2021.4版本发布以来,您不再需要编写单独的hyperopt类,而可以直接在策略中配置参数。 旧的方法仍然受支持,但不再是设置hyperopt的推荐方式。 旧版本文档请参考旧版Hyperopt

安装hyperopt的依赖项

由于Hyperopt的依赖项不需要用于运行机器人自身,它们较为庞大,无法在某些平台(如Raspberry Pi)上轻松构建,因此它们不会默认安装。在运行Hyperopt之前,您需要按照下面的步骤安装相应的依赖项。

Note

由于Hyperopt是一种消耗资源的过程,在Raspberry Pi上运行它不被推荐也不受支持。

Dockerdocker-image 包含了 hyperopt 的依赖关系,无需进一步操作。

简易安装脚本 (setup.sh) / 手动安装

source .venv/bin/activate
pip install -r requirements-hyperopt.txt

Hyperopt 命令参考

使用方法:freqtrade hyperopt [-h] [-v] [--logfile FILE] [-V] [-c PATH] [-d PATH]
                          [--userdir PATH] [-s NAME] [--strategy-path PATH]
                          [--recursive-strategy-search] [--freqaimodel NAME]
                          [--freqaimodel-path PATH] [-i TIMEFRAME]
                          [--timerange TIMERANGE]
                          [--data-format-ohlcv {json,jsongz,hdf5}]
                          [--max-open-trades INT]
                          [--stake-amount STAKE_AMOUNT] [--fee FLOAT]
                          [-p PAIRS [PAIRS ...]] [--hyperopt-path PATH]
                          [--eps] [--dmmp] [--enable-protections]
                          [--dry-run-wallet DRY_RUN_WALLET]
                          [--timeframe-detail TIMEFRAME_DETAIL] [-e INT]
                          [--spaces {all,buy,sell,roi,stoploss,trailing,protection,trades,default} [{all,buy,sell,roi,stoploss,trailing,protection,trades,default} ...]]
                          [--print-all] [--no-color] [--print-json] [-j JOBS]
                          [--random-state INT] [--min-trades INT]
                          [--hyperopt-loss NAME] [--disable-param-export]
                          [--ignore-missing-spaces] [--analyze-per-epoch]

可选参数:
  -h, --help            显示此帮助消息并退出
  -i TIMEFRAME, --timeframe TIMEFRAME
                        指定时间框架 (`1m`, `5m`, `30m`, `1h`, `1d`)。
  --timerange TIMERANGE
                        指定要使用的数据时间范围。
  --data-format-ohlcv {json,jsongz,hdf5}
                        存储下载的蜡烛(OHLCV)数据的格式。
                        (默认值:`json`)。
  --max-open-trades INT
                        覆盖 `max_open_trades` 配置设置的值。
  --stake-amount STAKE_AMOUNT
                        覆盖 `stake_amount` 配置设置的值。
  --fee FLOAT           指定费率比例。将应用两次(在交易进入和退出时)。
  -p PAIRS [PAIRS ...], --pairs PAIRS [PAIRS ...]
                        限制命令仅作用于这些交易对。交易对之间使用空格分隔。
  --hyperopt-path PATH  指定 Hyperopt 损失函数的附加查找路径。
  --eps, --enable-position-stacking
                        允许对相同的交易对进行多次购买(仓位堆叠)。
  --dmmp, --disable-max-market-positions
                        在回测期间禁用 `max_open_trades` 的应用(与将 `max_open_trades` 设置为一个很大的数值相同)。
  --enable-protections, --enableprotections
                        启用回测保护功能。会大幅减慢回测速度,但会包含配置的保护机制。
  --dry-run-wallet DRY_RUN_WALLET, --starting-balance DRY_RUN_WALLET
                        起始资金,用于回测/超参数优化和模拟交易。
  --timeframe-detail TIMEFRAME_DETAIL
                        指定回测的详细时间框架(`1m`, `5m`, `30m`, `1h`, `1d`)。
  -e INT, --epochs INT  指定迭代次数(默认值:100)。
  --spaces {all,buy,sell,roi,stoploss,trailing,protection,trades,default} [{all,buy,sell,roi,stoploss,trailing,protection,trades,default} ...]
                        指定要进行超参数优化的参数。以空格分隔的列表。
  --print-all           打印所有结果,而不仅仅是最佳结果。
  --no-color            禁用超参数优化结果的着色。如果将输出重定向到文件中可能有用。
  --print-json          以 JSON 格式打印输出。
  -j JOBS, --job-workers JOBS
                        并行运行的作业数以进行超参数优化(超参数优化工作进程)。如果为 -1(默认值),则使用所有 CPU;对于 -2,使用除一个之外的所有 CPU,依此类推。如果给出了 1,则根本不使用并行计算代码。
  --random-state INT    将随机状态设置为正整数以获得可重复的超参数优化结果。
  --min-trades INT      设置期望的最小交易次数以进行超参数优化评估(默认值:1)。
  --hyperopt-loss NAME, --hyperoptloss NAME
                        指定 hyperopt loss 函数类别名(IHyperOptLoss)。不同的函数可以生成完全不同的结果,因为优化的目标不同。内置的 Hyperopt 损失函数包括:
                        ShortTradeDurHyperOptLoss, OnlyProfitHyperOptLoss,
                        SharpeHyperOptLoss, SharpeHyperOptLossDaily,
                        SortinoHyperOptLoss, SortinoHyperOptLossDaily,
                        CalmarHyperOptLoss, MaxDrawDownHyperOptLoss,
                        MaxDrawDownRelativeHyperOptLoss,
                        ProfitDrawDownHyperOptLoss
  --disable-param-export
                        禁用自动超参数导出功能。
  --ignore-missing-spaces, --ignore-unparameterized-spaces
                        对于任何请求的 Hyperopt spaces 中不包含任何参数的情况,不显示错误。
  --analyze-per-epoch   每个迭代运行 populate_indicators 一次。

常用参数:
  -v, --verbose         详细模式(-vv 以获得更多信息,-vvv 可获得所有消息)。
  --logfile FILE        记录到指定的文件中。特殊值有:“syslog”、“journald”。有关详细信息,请参阅文档。
  -V, --version         显示程序的版本号并退出
  -c PATH, --config PATH
                        指定配置文件(默认值:`userdir/config.json` 或 `config.json`,以存在的文件为准)。可以使用多个 --config 选项。可以设置为 `-` 以从标准输入读取配置。
  -d PATH, --datadir PATH
                        包含历史回测数据的目录路径。
  --userdir PATH, --user-data-dir PATH
                        用户数据目录的路径。

策略参数:
  -s NAME, --strategy NAME
                        指定机器人将使用的策略类名。
  --strategy-path PATH  指定额外的策略查找路径。
  --recursive-strategy-search
                        递归在策略文件夹中搜索策略。
  --freqaimodel NAME    指定自定义 freqaimodels。
  --freqaimodel-path PATH
                        指定 freqaimodels 的附加查找路径。

Hyperopt 核对清单所有任务/可能性的超参数调优清单

根据您想要优化的空间,只需要以下其中一些:

  • 通过 space='buy' 定义参数 - 用于进场信号的优化
  • 通过 space='sell' 定义参数 - 用于出场信号的优化

注意

populate_indicators 需要创建所有空间可能使用的指标,否则 hyperopt 将无法工作。

很少情况下,您可能还需要创建一个名为 HyperOpt嵌套类并实现以下方法:

  • roi_space - 用于自定义 ROI 优化(如果您需要优化超空间中 ROI 参数的范围与默认值不同)
  • generate_roi_table - 用于自定义 ROI 优化(如果您需要优化 ROI 表中的值的范围与默认值不同,或者 ROI 表中的条目数(步数)与默认的 4 步不同)
  • stoploss_space - 用于自定义止损优化(如果您需要优化超空间中止损参数的范围与默认值不同)
  • trailing_space - 用于自定义追踪止损优化(如果您需要优化超空间中追踪止损参数的范围与默认值不同)
  • max_open_trades_space - 用于自定义最大开仓交易数优化(如果您需要优化超空间中最大开仓交易数参数的范围与默认值不同)

快速优化 ROI、止损和追踪止损

您可以快速优化 roistoplosstrailing 空间,而无需更改策略中的任何内容。

# 准备一个可用的策略。
freqtrade hyperopt --hyperopt-loss SharpeHyperOptLossDaily --spaces roi stoploss trailing --strategy MyWorkingStrategy --config config.json -e 100

Hyperopt 执行逻辑

Hyperopt 首先将数据加载到内存中,然后对每个交易对运行 populate_indicators() 一次以生成所有指标,除非指定了 --analyze-per-epoch。Hyperopt然后将在不同的进程中生成(处理器数量,或-j <n>),并一次又一次地对--spaces定义的参数进行回测。

对于每组新的参数,freqtrade将首先运行populate_entry_trend(),然后是populate_exit_trend(),然后运行常规的回测过程以模拟交易。

回测之后,结果将传递给损失函数,该函数将评估此结果是更好还是更差。根据损失函数的结果,hyperopt将确定下一轮回测中要尝试的参数。

配置您的守卫和触发器

在您的策略文件中,您需要更改两个位置以添加用于测试的新购买hyperopt:

  • 在类级别定义hyperopt将优化的参数。
  • populate_entry_trend()中使用定义的参数值而不是原始常量。

在那里,您有两种不同类型的指标:1. 守卫和2. 触发器

  1. 守卫是条件,例如“如果ADX < 10,则永远不要购买”,或者如果当前价格超过EMA10,则永远不要购买。
  2. 触发器是在特定时刻触发购买的那些信号,例如“当EMA5穿过EMA10时购买”或“当收盘价格触及下限布林带时购买”。

守卫和触发器

从技术上讲,守卫和触发器之间没有区别。 但是,本指南将进行这种区分,以明确信号不应该是“粘性”的。 粘性信号是在多个蜡烛期内都是有效的信号。这可能导致在信号消失之前进入信号(这意味着成功的机会比一开始时要低得多)。

对于每个时期轮,超级优化将选择一个触发器和可能多个守卫。

退出信号优化

和上面的入场信号一样,退出信号也可以进行优化。将相应的设置放在以下方法中:

  • 在类级别定义要由hyperopt优化的参数,可以将它们命名为sell_*,或者通过显式定义space='sell'来进行。
  • populate_exit_trend()方法中,使用定义的参数值而不是原始常量。

配置和规则与买入信号相同。

解开谜团

假设你好奇:在触发做多位置时,是应使用MACD交叉还是较低的布林带?同时还想知道是否应使用RSI或ADX来辅助做出这些决策。如果决定使用RSI或ADX,应该使用哪些值呢?

因此,让我们使用超参数优化来揭开这个谜团。

定义要使用的指标

首先,我们要计算我们的策略将使用的指标。

class MyAwesomeStrategy(IStrategy):

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        生成策略所需的所有指标
        """
        dataframe['adx'] = ta.ADX(dataframe)
        dataframe['rsi'] = ta.RSI(dataframe)
        macd = ta.MACD(dataframe)
        dataframe['macd'] = macd['macd']
        dataframe['macdsignal'] = macd['macdsignal']
        dataframe['macdhist'] = macd['macdhist']

        bollinger = ta.BBANDS(dataframe, timeperiod=20, nbdevup=2.0, nbdevdn=2.0)
        dataframe['bb_lowerband'] = bollinger['lowerband']
        dataframe['bb_middleband'] = bollinger['middleband']
        dataframe['bb_upperband'] = bollinger['upperband']
        return dataframe

Hyperoptable参数

我们继续定义hyperoptable参数:

class MyAwesomeStrategy(IStrategy):
    buy_adx = DecimalParameter(20, 40, decimals=1, default=30.1, space="buy")
    buy_rsi = IntParameter(20, 40, default=30, space="buy")
    buy_adx_enabled = BooleanParameter(default=True, space="buy")
    buy_rsi_enabled = CategoricalParameter([True, False], default=False, space="buy")
    buy_trigger = CategoricalParameter(["bb_lower", "macd_cross_signal"], default="bb_lower", space="buy")

上面的定义表示:我有五个参数,我想随机组合它们以找到最佳组合。 buy_rsi 是一个整数参数,将在 20 到 40 之间进行测试。此空间的大小为 20。 buy_adx 是一个小数参数,将在 20 到 40 之间以一位小数进行评估(因此值为 20.1,20.2,...)。此空间的大小为 200。 然后我们有三个分类变量。前两个要么是 True,要么是 False。 我们使用它们来启用或禁用 ADX 和 RSI 保护。 我们称最后一个为 trigger,使用它来决定我们要使用哪个买入触发器。

参数空间分配

参数必须被分配给一个名为 buy_*sell_* 的变量 - 或包含 space='buy' | space='sell',以便正确分配到一个空间。 如果某个空间没有可用的参数,那么在运行 hyperopt 时会收到未找到空间的错误。 具有不明确空间的参数(例如 adx_period = IntParameter(4, 24, default=14) - 没有显式或隐式空间)将不被检测,因此将被忽略。

所以让我们使用这些值来编写买入策略:

    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        conditions = []
        # 守卫和趋势
        if self.buy_adx_enabled.value:
            conditions.append(dataframe['adx'] > self.buy_adx.value)
        if self.buy_rsi_enabled.value:
            conditions.append(dataframe['rsi'] < self.buy_rsi.value)

        # 触发器
        if self.buy_trigger.value == 'bb_lower':
            conditions.append(dataframe['close'] < dataframe['bb_lowerband'])
        if self.buy_trigger.value == 'macd_cross_signal':
            conditions.append(qtpylib.crossed_above(
                dataframe['macd'], dataframe['macdsignal']
            ))# 检查体积不为0
conditions.append(dataframe['volume'] > 0)

if conditions:
    dataframe.loc[
        reduce(lambda x, y: x & y, conditions),
        'enter_long'] = 1

return dataframe

Hyperopt将以不同的值组合多次(epochs)调用 populate_entry_trend()函数. 它将使用给定的历史数据,并根据上述函数生成的买入信号模拟买入。 根据结果,hyperopt将告诉您哪个参数组合产生了最佳结果(根据配置的损失函数)。

注意

上述设置期望在已填充的指标中找到ADX,RSI和Bollinger Bands。 当您想要测试机器人当前未使用的指标时,请记得将其添加到策略或hyperopt文件中的 populate_indicators()方法中。

参数类型

有四种参数类型,每种类型适用于不同的目的。

  • IntParameter - 定义了一个整数参数,具有搜索空间的上下边界。
  • DecimalParameter - 定义了带有有限小数位数(默认为3位)的浮点数参数。在大多数情况下,应首选此参数类型,而不是RealParameter
  • RealParameter - 定义了一个带有上下边界和无精度限制的浮点数参数。由于它创建了一个接近无限的可能性空间,因此很少使用。
  • CategoricalParameter - 定义了一个具有预定选项数量的参数。
  • BooleanParameter - CategoricalParameter([True, False])的简写形式-非常适合"启用"参数。

参数选项

有两个参数选项可以帮助您快速测试各种想法:* optimize - 当设置为 False 时,该参数将不会被包含在优化过程中。(默认值:True) * load - 当设置为 False 时,在后续的超参数优化中将不会使用之前超优化运行中的结果(无论是在您的策略中的 buy_paramssell_params 中,还是在 JSON 输出文件中)作为起始值。而是使用参数中指定的默认值。(默认值:True)

load=False 在回测中的影响

请注意,将 load 选项设置为 False 将意味着回测也将使用参数中指定的默认值,而不是通过超优化找到的值。

Warning

不能在 populate_indicators 中使用超优化参数 - 因为超优化不会为每个 epoch 重新计算指标,所以在这种情况下将使用起始值。

优化指标参数

假设您有一个简单的策略在脑海中 - 一个 EMA 交叉策略(两个移动平均线交叉) - 您想要为该策略找到最理想的参数。 默认情况下,我们默认止损为 5% - 和拿到盈利率(minimal_roi)为 10% - 这意味着 freqtrade 将在获得 10% 的盈利后卖出交易。

from pandas import DataFrame
from functools import reduce

import talib.abstract as ta

from freqtrade.strategy import (BooleanParameter, CategoricalParameter, DecimalParameter, 
                                IStrategy, IntParameter)
import freqtrade.vendor.qtpylib.indicators as qtpylib

class MyAwesomeStrategy(IStrategy):
    stoploss = -0.05
    timeframe = '15m'
    minimal_roi = {
        "0":  0.10
    }
    # 定义参数空间
    buy_ema_short = IntParameter(3, 50, default=5)
    buy_ema_long = IntParameter(15, 200, default=50)


    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """生成策略中使用的所有指标"""

        # 计算所有 ema_short 值
        for val in self.buy_ema_short.range:
            dataframe[f'ema_short_{val}'] = ta.EMA(dataframe, timeperiod=val)

        # 计算所有 ema_long 值
        for val in self.buy_ema_long.range:
            dataframe[f'ema_long_{val}'] = ta.EMA(dataframe, timeperiod=val)

        return dataframe


    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        conditions = []
        conditions.append(qtpylib.crossed_above(
                dataframe[f'ema_short_{self.buy_ema_short.value}'], dataframe[f'ema_long_{self.buy_ema_long.value}']
            ))

        # 检查成交量是否不为0
        conditions.append(dataframe['volume'] > 0)

        if conditions:
            dataframe.loc[
                reduce(lambda x, y: x & y, conditions),
                'enter_long'] = 1
        return dataframe

    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        conditions = []
        conditions.append(qtpylib.crossed_above(
                dataframe[f'ema_long_{self.buy_ema_long.value}'], dataframe[f'ema_short_{self.buy_ema_short.value}']
            ))

        # 检查成交量是否不为0
        conditions.append(dataframe['volume'] > 0)

        if conditions:
            dataframe.loc[
                reduce(lambda x, y: x & y, conditions),
                'exit_long'] = 1
        return dataframe

分解一下:

使用 self.buy_ema_short.range 将返回包含参数 lowhigh 之间所有条目的范围对象。 在这种情况下 (IntParameter(3, 50, default=5)),循环将运行从 3 到 50 的所有数字 ([3, 4, 5, ... 49, 50])。 通过在循环中使用该值,hyperopt 将生成48个新的列 (['buy_ema_3', 'buy_ema_4', ... , 'buy_ema_50']).

Hyperopt 本身将使用所选的值来创建买入和卖出信号。

虽然这个策略很可能过于简单,无法提供持续的利润,但它应该作为优化指标参数的示例。!!! 注意 在超参优化模式中,self.buy_ema_short.range 的行为与其他模式有所不同。对于超参优化模式,上述示例可能会生成48个新的列;而对于其他所有模式(回测、模拟/实盘),它只会生成所选值的列。因此,您应避免使用具有明确值(即self.buy_ema_short.value之外的值)的结果列。

注意

range 属性也可以用于 DecimalParameterCategoricalParameter。由于无限搜索空间,RealParameter 不提供此属性。

性能提示

在正常的超参优化过程中,指标只计算一次,并在每个周期提供给其他函数,随着核心数量的增加,内存使用量呈线性增长。因为这也会影响性能,有两种方法可以减少内存使用量:

  • ema_shortema_long 的计算从 populate_indicators() 移动到 populate_entry_trend()。由于 populate_entry_trend() 会在每个周期计算一次,您不需要使用 .range 功能。
  • 超参优化提供了 --analyze-per-epoch 选项,它将执行 populate_indicators() 移动到周期过程中,每个周期只计算一次参数的单个值,而不是使用 .range 功能。在这种情况下,.range 功能仅返回实际使用的值。

这些方法会减少内存使用量,但会增加 CPU 使用量。但是,由于内存不足(OOM)问题,您的超参优化运行不太可能失败。

无论您使用 .range 功能还是上述的替代方法,您应该尽量使用尽可能小的空间范围,因为这将改善 CPU/RAM 使用情况。

优化保护措施

Freqtrade 也可以优化保护措施。如何优化保护措施取决于您,以下内容仅供参考。

策略只需要定义"protections"入口,返回一个包含保护配置的列表。

from pandas import DataFrame
from functools import reduce

import talib.abstract as ta

from freqtrade.strategy import (BooleanParameter, CategoricalParameter, DecimalParameter, 
                                IStrategy, IntParameter)
import freqtrade.vendor.qtpylib.indicators as qtpylib

class MyAwesomeStrategy(IStrategy):
    stoploss = -0.05
    timeframe = '15m'
    # 定义参数空间
    cooldown_lookback = IntParameter(2, 48, default=5, space="保护", optimize=True)
    stop_duration = IntParameter(12, 200, default=5, space="保护", optimize=True)
    use_stop_protection = BooleanParameter(default=True, space="保护", optimize=True)


    @property
    def protections(self):
        prot = []

        prot.append({
            "method": "CooldownPeriod",
            "stop_duration_candles": self.cooldown_lookback.value
        })
        if self.use_stop_protection.value:
            prot.append({
                "method": "StoplossGuard",
                "lookback_period_candles": 24 * 3,
                "trade_limit": 4,
                "stop_duration_candles": self.stop_duration.value,
                "only_per_pair": False
            })

        return prot

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        # ...

然后可以像下面这样运行hyperopt: freqtrade hyperopt --hyperopt-loss SharpeHyperOptLossDaily --strategy MyAwesomeStrategy --spaces protection

注意

保护空间不是默认空间的一部分,只能在Parameters Hyperopt接口中使用,而不能在旧的hyperopt接口中使用(旧接口需要单独的hyperopt文件)。 如果选择了保护空间,Freqtrade还会自动更改"--enable-protections"标志。

警告

如果保护定义为属性,配置中的条目将被忽略。 因此建议不要在配置中定义保护。

从先前的属性设置迁移

从先前的设置迁移非常简单,只需将protections条目转换为属性即可。 简单来说,以下配置将转换为以下形式。

class MyAwesomeStrategy(IStrategy):
    protections = [
        {
            "method": "CooldownPeriod",
            "stop_duration_candles": 4
        }
    ]

结果

class MyAwesomeStrategy(IStrategy):

    @property
    def protections(self):
        return [
            {
                "method": "CooldownPeriod",
                "stop_duration_candles": 4
            }
        ]

您随后还会将潜在有趣的条目更改为参数以允许超级优化。

优化max_entry_position_adjustment

虽然max_entry_position_adjustment不是一个独立的空间,但可以使用上面显示的属性方法在hyperopt中使用。

from pandas import DataFrame
from functools import reduce

import talib.abstract as ta

from freqtrade.strategy import (BooleanParameter, CategoricalParameter, DecimalParameter, 
                                IStrategy, IntParameter)
import freqtrade.vendor.qtpylib.indicators as qtpylib

class MyAwesomeStrategy(IStrategy):
    stoploss = -0.05
    timeframe = '15m'

    # 定义参数空间
    max_epa = CategoricalParameter([-1, 0, 1, 3, 5, 10], default=1, space="buy", optimize=True)

    @property
    def max_entry_position_adjustment(self):
        return self.max_epa.value


    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        # ...
使用 IntParameter

你也可以使用 IntParameter 进行优化,但是你必须显式地返回一个整数:

max_epa = IntParameter(-1, 10, default=1, space="buy", optimize=True)

@property
def max_entry_position_adjustment(self):
    return int(self.max_epa.value)

损失函数

每个超参数调整都需要一个目标。通常这被定义为损失函数(有时也被称为目标函数),它应该随着更理想的结果而减小,随着不好的结果而增加。

通过 --hyperopt-loss <Class-name> 参数(或者可选地通过配置文件中的 "hyperopt_loss" 键)来指定损失函数。该类应位于 user_data/hyperopts/ 目录中的单独文件中。目前内置了以下损失函数:

  • ShortTradeDurHyperOptLoss -(默认的遗留 Freqtrade 超参数优化损失函数) - 主要用于短期交易持续时间和避免亏损。
  • OnlyProfitHyperOptLoss - 仅考虑利润金额。
  • SharpeHyperOptLoss - 优化基于交易收益相对于标准差的夏普比率。
  • SharpeHyperOptLossDaily - 优化基于每日交易收益与标准差的夏普比率。
  • SortinoHyperOptLoss - 优化基于交易收益相对于下行标准差的索提诺比率。
  • SortinoHyperOptLossDaily - 优化基于每日交易收益与下行标准差的索提诺比率。
  • MaxDrawDownHyperOptLoss - 优化最大绝对回撤。
  • MaxDrawDownRelativeHyperOptLoss - 同时优化最大绝对回撤和最大相对回撤。
  • CalmarHyperOptLoss - 优化基于交易收益相对于最大回撤的卡尔马比率。
  • ProfitDrawDownHyperOptLoss - 通过最大利润和最小回撤目标进行优化。可以调整 hyperoptloss 文件中的 DRAWDOWN_MULT 变量以在回撤目的上更加严格或灵活。

在文档的高级超参优化部分介绍了如何创建自定义损失函数。

执行超参优化

一旦你更新了超参优化配置,就可以运行它了。 由于超参优化会尝试很多组合来找到最佳参数,所以需要一些时间来获得好的结果。

我们强烈建议使用 screentmux 来防止连接丢失。

freqtrade hyperopt --config config.json --hyperopt-loss <hyperoptlossname> --strategy <strategyname> -e 500 --spaces all

-e 选项将设置 hyperopt 进行的评估次数。由于 hyperopt 使用贝叶斯搜索,一次运行过多的 epochs 可能不会产生更好的结果。经验表明,500-1000 个 epochs 之后,最佳结果通常不会有太多改善。 使用几千个 epochs 进行多次运行(执行)并使用不同的随机状态很可能会产生不同的结果。

--spaces all 选项确定应该优化所有可能的参数。下面列出了可能性。

注意

Hyperopt 将使用超参优化开始时间的时间戳存储超参优化结果。 读取命令(hyperopt-listhyperopt-show)可以使用 --hyperopt-filename <filename> 来读取和显示旧的超参优化结果。 可以使用 ls -l user_data/hyperopt_results/ 查找文件名列表。

使用不同的历史数据源执行Hyperopt

如果您想使用备份在磁盘上的替代历史数据集来优化参数,可以使用--datadir PATH选项。 默认情况下,Hyperopt使用来自user_data/data目录的数据。

用较小的测试集运行Hyperopt

使用--timerange参数来更改您想使用的测试集的大小。 例如,要使用一个月的数据,将--timerange 20210101-20210201(从2021年1月到2021年2月)传递给Hyperopt调用。

完整命令:

freqtrade hyperopt --strategy <strategyname> --timerange 20210101-20210201

使用较小的搜索空间运行Hyperopt

使用--spaces选项来限制Hyperopt使用的搜索空间。 让Hyperopt优化所有参数是一个巨大的搜索空间。 通常,最合理的做法是从仅搜索初始买入算法开始。 或者,您可能只想为您的新买策略优化止损或ROI表。

合法的值包括:

  • all:优化所有参数
  • buy:仅搜索新的买入策略
  • sell:仅搜索新的卖出策略
  • roi:仅优化策略的最小利润表
  • stoploss:搜索最佳止损值
  • trailing:搜索最佳追踪止损值
  • trades:搜索最佳最大持仓交易数值
  • protection:搜索最佳的保护参数(请阅读保护部分以了解如何正确定义这些参数)
  • defaultall,但不包括trailingprotection
  • 以空格分隔的上述任意值的列表,例如--spaces roi stoploss默认的 Hyperopt 搜索空间在没有指定 --space 命令行选项时不包括 trailing 空间。建议在找到其他空间的最佳参数、验证并粘贴到自定义策略后,单独运行 trailing 空间的优化。

理解 Hyperopt 结果

一旦 Hyperopt 完成,你可以使用结果来更新你的策略。给定以下来自 Hyperopt 的结果:

最佳结果:

    44/100:     135 次交易。平均利润 0.57%。总利润 0.03871918 BTC(0.7722%)。平均持续时间 180.4 分钟。目标值:1.94367

    # 购买超空间参数:
    buy_params = {
        'buy_adx': 44,
        'buy_rsi': 29,
        'buy_adx_enabled': False,
        'buy_rsi_enabled': True,
        'buy_trigger': 'bb_lower'
    }

你应该理解这个结果如下:

  • 最佳的购买触发器是 bb_lower
  • 你不应该使用 ADX,因为 'buy_adx_enabled': False
  • 你应该考虑使用 RSI 指标('buy_rsi_enabled': True),而最佳数值为 29.0'buy_rsi': 29.0)。

自动应用参数到策略

当使用 Hyperoptable 参数时,你的 hyperopt-run 的结果将被写入到策略旁边的一个 json 文件中(对于 MyAwesomeStrategy.py,文件名将是 MyAwesomeStrategy.json)。 除非使用 --disable-param-export 提供给这两个命令中的任意一个,否则这个文件在使用 hyperopt-show 子命令时也会被更新。您的策略类也可以明确地包含这些结果。只需复制hyperopt结果块,并将其粘贴到类级别,在此过程中替换旧参数(如果有的话)。下一次执行策略时,新参数将自动加载。

将整个hyperopt结果转移到您的策略中如下所示:

class MyAwesomeStrategy(IStrategy):
    # Buy hyperspace params:
    buy_params = {
        'buy_adx': 44,
        'buy_rsi': 29,
        'buy_adx_enabled': False,
        'buy_rsi_enabled': True,
        'buy_trigger': 'bb_lower'
    }

注意

配置文件中的值将覆盖参数文件级别的参数 - 并且两者都将覆盖策略内的参数。 因此,请遵循以下优先级顺序:配置文件 > 参数文件 > 策略 *_params > 参数默认值

理解Hyperopt ROI结果

如果您正在优化ROI(即如果优化搜索空间包含'all'、'default'或'roi'),则您的结果将如下所示,并包括ROI表格:

最佳结果:

    44/100:    135笔交易。平均利润0.57%。总利润0.03871918 BTC(0.7722%)。平均持续时间180.4分钟。目标值:1.94367

    # ROI表格:
    minimal_roi = {
        0: 0.10674,
        21: 0.09158,
        78: 0.03634,
        118: 0
    }

为了在回测和实盘交易/模拟运行中使用Hyperopt找到的最佳ROI表格,请将其复制粘贴为自定义策略的minimal_roi属性的值:

    # 为策略设计的最小 ROI。
    # 如果配置文件中包含 "minimal_roi",则该属性将被覆盖。
    minimal_roi = {
        0: 0.10674,
        21: 0.09158,
        78: 0.03634,
        118: 0
    }

正如注释中所述,您还可以将其用作配置文件中 minimal_roi 设置的值。

默认 ROI 搜索空间

如果您要优化 ROI,Freqtrade 会为您创建一个 'roi' 优化超空间 - 它是 ROI 表的组成部分的超空间。默认情况下,Freqtrade 生成的每个 ROI 表由 4 行(步骤)组成。Hyperopt 对使用的时间框架的 ROI 步骤中的值范围实现了自适应范围。默认情况下,各个值的范围如下(对于一些常用的时间框架,值被四舍五入到小数点后的三位):

# 步骤 1m 5m 1h 1d
1 0 0.011...0.119 0 0.03...0.31 0 0.068...0.711 0 0.121...1.258
2 2...8 0.007...0.042 10...40 0.02...0.11 120...480 0.045...0.252 2880...11520 0.081...0.446
3 4...20 0.003...0.015 20...100 0.01...0.04 240...1200 0.022...0.091 5760...28800 0.040...0.162
4 6...44 0.0 30...220 0.0 360...2640 0.0 8640...63360 0.0

这些范围在大多数情况下应该足够了。步骤(ROI 字典键)中的分钟数与使用的时间框架成线性比例。步骤(ROI 字典值)中的 ROI 值与使用的时间框架成对数比例。

如果您在自定义超优化中使用了 generate_roi_table()roi_space() 方法,请将它们移除,以利用 Freqtrade 默认生成的自适应 ROI 表和 ROI 超优化空间。

如果您需要使 ROI 表的组成部分在其他范围内变化,请重写 roi_space() 方法。如果您需要不同结构的 ROI 表或其他行数(步骤),请重写 generate_roi_table()roi_space() 方法,并实现您自己的自定义 ROI 表生成方法。

关于这些方法的示例可以在 覆盖预定义空间章节 中找到。

减小搜索空间

为了进一步限制搜索空间,小数点后的位数被限制为 3 位(精度为 0.001)。通常情况下,这已足够,比此更精确的值往往会导致过拟合的结果。但是,您可以通过 覆盖预定义空间 来根据您的需求进行更改。

理解 Hyperopt 止损结果

如果你正在优化止损值(即如果优化搜索空间包含'all','default'或'stoploss'),你的结果将如下所示,并包括止损:

最佳结果:

    44/100:    135 次交易。平均利润 0.57%。总利润 0.03871918 BTC(0.7722%)。平均持续时间 180.4 分钟。目标:1.94367

    # 购买超参数:
    buy_params = {
        'buy_adx': 44,
        'buy_rsi': 29,
        'buy_adx_enabled': False,
        'buy_rsi_enabled': True,
        'buy_trigger': 'bb_lower'
    }

    止损值:-0.27996

为了在回测和实盘交易/模拟交易中使用 Hyperopt 找到的最佳止损值,请将其复制粘贴为自定义策略的 stoploss 属性的值:

    # 为策略设计的最佳止损值
    # 如果配置文件包含 "stoploss",这个属性将被覆盖
    stoploss = -0.27996

正如注释中所述,你也可以将其作为配置文件中 stoploss 设置的值。

默认止损搜索空间

如果你正在优化止损值,Freqtrade会为你创建一个名为stoploss的优化超参数空间。默认情况下,在该空间内的止损值变化范围为-0.35到-0.02,这在大多数情况下已足够。

如果你的自定义超参数文件中有stoploss_space()方法,请删除该方法,以便使用Freqtrade默认生成的止损超优化空间。

如果你需要在超参数优化期间使止损值在其他范围内变化,则需要重写stoploss_space()方法并在其中定义所需的范围。有关该方法的示例可以在重写预定义空间部分找到。

减少搜索空间

为了进一步限制搜索空间,小数将被限制为3位小数(精度为0.001)。这通常已足够使用,比这更精确的值通常会导致过度拟合的结果。然而,你可以通过重写预定义空间根据需要更改精度。

理解Hyperopt跟踪止损结果

如果你正在优化跟踪止损值(即优化搜索空间包含'all'或'trailing'),你的结果将如下所示,并包括跟踪止损参数:

最佳结果:

    45/100: 606次交易。平均利润1.04%。总利润0.31555614 BTC(630.48%)。平均持续时间150.3分钟。目标值:-1.10161

    # 跟踪止损:
    trailing_stop = True
    trailing_stop_positive = 0.02001
    trailing_stop_positive_offset = 0.06038
    trailing_only_offset_is_reached = True

为了在回测和实时交易/模拟交易中使用Hyperopt找到的最佳跟踪止损参数,请将它们复制粘贴为你自定义策略的相应属性的值:

    # 止损追踪
    # 如果配置文件包含相应的值,这些属性将会被覆盖。
    trailing_stop = True
    trailing_stop_positive = 0.02001
    trailing_stop_positive_offset = 0.06038
    trailing_only_offset_is_reached = True

正如注释所述,您也可以将其用作配置文件中相应设置的值。

默认的止损追踪搜索空间

如果您正在优化跟踪止损值,Freqtrade会为您创建“trailing”优化超空间。默认情况下,在该超空间中,trailing_stop参数始终设置为True,trailing_only_offset_is_reached的值在True和False之间变化,trailing_stop_positivetrailing_stop_positive_offset参数的值相应地在0.02至0.35范围和0.01至0.1范围内变化,这在大多数情况下已经足够了。

如果您需要在超优化期间使跟踪止损参数的值在其他范围内变化,请覆盖trailing_space()方法并在其中定义所需的范围。您可以在覆盖预定义空间部分中找到该方法的示例。

缩小搜索空间

为了进一步限制搜索空间,小数点后的位数被限制为3位小数(精度为0.001)。这通常足够了,比这更精确的值通常会导致过拟合的结果。 但是,您可以通过覆盖预定义空间来根据自己的需求进行更改。

可重现的结果

寻找最佳参数的搜索从一些(当前为30种)随机组合开始,在参数的超空间中进行随机的Hyperopt迭代。这些随机迭代在Hyperopt输出的第一列中用星号字符(*)标记。

生成这些随机值的初始状态(随机状态)由--random-state命令行选项的值控制。您可以将其设置为您选择的任意值,以获得可重复的结果。

如果您在命令行选项中没有显式设置这个值,Hyperopt会为您的随机状态种子分配一个随机值。每个Hyperopt运行的随机状态值都显示在日志中,因此您可以将其复制并粘贴到--random-state命令行选项中,以重复使用所使用的初始随机迭代集。如果您在命令行选项、配置、时间范围、策略和 Hyperopt 类、历史数据和损失函数中没有做任何更改,使用相同的随机状态值,您应该可以获得相同的超参数优化结果。

输出格式

默认情况下,hyperopt 会以带颜色的形式打印结果。盈利的回合将以绿色打印,这有助于您找到以后进行分析的回合。总利润为零或负利润的回合将以正常的颜色打印。如果您不需要结果的颜色(例如,当您将 hyperopt 输出重定向到文件时),您可以在命令行中指定 --no-color 选项来关闭颜色化。

您可以使用 --print-all 命令行选项,以查看 hyperopt 输出中的所有结果,而不仅仅是最佳结果。当使用 --print-all 选项时,默认情况下,当前最佳结果也会进行颜色化 - 它们将以粗体(亮)样式打印。这也可以通过 --no-color 命令行选项来关闭。

Windows 和颜色输出

Windows 不支持本地颜色输出,因此自动禁用。为了在 Windows 上获得 hyperopt 的颜色输出,请考虑使用 WSL。

位置堆叠和禁用最大市场仓位

在某些情况下,您可能需要使用 --eps/--enable-position-staking--dmmp/--disable-max-market-positions 参数运行 Hyperopt(和回测)。

默认情况下,hyperopt 模拟 Freqtrade 实盘运行/模拟运行的行为,在每个交易对中只允许有一个开放的交易。对于所有交易对来说,同时开放的交易总数也受到 max_open_trades 设置的限制。在 Hyperopt/回测期间,这可能导致一些潜在的交易被先前开放的交易所掩盖。

--eps/--enable-position-staking 参数允许模拟多次购买同一交易对的行为,而 --dmmp/--disable-max-market-positions 则在 Hyperopt/回测期间禁用 max_open_trades 的应用(这等同于将 max_open_trades 设置为一个非常大的数字)。

注意

实盘/模拟运行将不会使用位置堆叠 - 因此,也可以验证不使用此功能的策略,因为这更接近于真实情况。您可以在配置文件中通过显式设置"position_stacking"=true来启用位置堆叠。

内存不足错误

由于hyperopt消耗大量内存(完整数据必须在每个并行回测过程中一次性加载到内存中),您很可能会遇到"内存不足"错误。 为了应对这些错误,您有多个选项:

  • 减少交易对数量。
  • 减少使用的时间范围(--timerange <timerange>)。
  • 避免使用--timeframe-detail(这会将大量附加数据加载到内存中)。
  • 减少并行进程数(-j <n>)。
  • 增加机器的内存。
  • 如果使用了大量具有.range功能的参数,请使用--analyze-per-epoch

目标已在此处进行了评估。

如果您看到The objective has been evaluated at this point before. - 那么这意味着您的空间已用尽或接近用尽。 基本上,您的空间中的所有点都已被触及(或已达到局部最小值)- hyperopt在多维空间中不再找到未尝试的点。 Freqtrade在这种情况下尝试通过使用新的随机点来解决"局部最小值"问题。

示例:

buy_ema_short = IntParameter(5, 20, default=10, space="buy", optimize=True)
# 这是buy空间中唯一的参数

buy_ema_short 空间有15个可能的值(5, 6, ... 19, 20)。如果您现在运行buy空间的hyperopt,hyperopt将在用完选项之前仅尝试这15个值。 因此,您的epochs应与可能的值对齐 - 或者,如果注意到大量The objective has been evaluated at this point before.警告,您应该准备中断运行。

显示Hyperopt结果的详细信息

在你运行 Hyperopt 进行了所需的训练轮数后,你可以随后列出所有结果以进行分析,仅选择最佳或最有利的一次,并显示以前评估过的任何训练轮次的详细信息。这可以通过使用 hyperopt-listhyperopt-show 子命令来实现。有关使用这些子命令的说明,请参阅 Utils 章节。

验证回测结果

一旦优化后的策略被应用到你的策略中,你应该进行回测来确保一切正常工作。

为了获得与 Hyperopt 过程中相同的结果(交易数量、交易持续时间、利润等),请使用与 Hyperopt 中相同的配置和参数(时间范围、时间框架等),用于回测的 --dmmp/--disable-max-market-positions--eps/--enable-position-stacking

为什么我的回测结果与 Hyperopt 结果不符?

如果结果不符合预期,请检查以下因素:

  • 可能已经在 populate_indicators() 中向 Hyperopt 添加了参数,这些参数将仅在所有训练轮次中计算一次。例如,如果您尝试优化多个 SMA 时间段值,则 hyperoptable 时间段参数应放置在 populate_entry_trend() 中,该函数在每个训练轮次都会计算。请参阅优化指标参数
  • 如果已禁用将 Hyperopt 参数自动导出到 JSON 参数文件,请仔细检查以确保正确地将所有 Hyperopt 值传递到你的策略中。
  • 检查日志以验证正在设置哪些参数和使用了哪些值。
  • 特别注意止损、最大开放交易和跟踪止损参数,因为这些参数通常在配置文件中设置,会覆盖对策略的更改。检查你的回测日志,确保配置文件没有不经意间设置了参数(如 stoplossmax_open_tradestrailing_stop)。
  • 确认您没有意外的参数 JSON 文件覆盖参数或策略中默认的 Hyperopt 设置。
  • 确认在回测中启用的任何保护措施在进行 Hyperopt 过程中是否也启用了,反之亦然。当使用 --space protection 时,保护措施会自动在进行 Hyperopt 过程中启用。