Skip to content

V2和V3之间的策略迁移

为了支持新的市场和交易类型(即带杠杆的短线交易),界面上必须进行一些更改。 如果您打算使用除现货市场之外的其他市场,请将策略迁移到新的格式。

我们花费了大量的精力来保持与现有策略的兼容性,因此如果您只想继续在现货市场中使用freqtrade,则目前不需要进行任何更改。

您可以使用快速摘要作为检查清单。请查阅下面的详细部分获取完整的迁移详细信息。

快速摘要/迁移清单

注意:forcesellforcebuyemergencysell 已更改为 force_exitforce_enteremergency_exit

详细说明

populate_buy_trend

populate_buy_trend() 中,您需要将分配的列从 'buy' 更改为 'enter_long',方法名称也应从 populate_buy_trend 更改为 populate_entry_trend

def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
    dataframe.loc[
        (
            (qtpylib.crossed_above(dataframe['rsi'], 30)) &  # 信号:RSI 上穿 30
            (dataframe['tema'] <= dataframe['bb_middleband']) &  # 保护条件
            (dataframe['tema'] > dataframe['tema'].shift(1)) &  # 保护条件
            (dataframe['volume'] > 0)  # 确保交易量不为0
        ),
        ['enter_long', 'enter_tag']] = (1, 'rsi_cross')

    return dataframe

请参考策略文档了解如何进入和退出短期交易。

populate_sell_trend

类似于 populate_buy_trendpopulate_sell_trend 将被重命名为 populate_exit_trend()。 我们还将把列从 'sell' 改为 'exit_long'

def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
    dataframe.loc[
        (
            (qtpylib.crossed_above(dataframe['rsi'], 70)) &  # 信号:RSI 上穿 70
            (dataframe['tema'] > dataframe['bb_middleband']) &  # 保护条件
            (dataframe['tema'] < dataframe['tema'].shift(1)) &  # 保护条件
            (dataframe['volume'] > 0)  # 确保交易量不为0
        ),
        ['exit_long', 'exit_tag']] = (1, 'some_exit_tag')
    return dataframe

之后

def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
    dataframe.loc[
        (
            (qtpylib.crossed_above(dataframe['rsi'], 70)) &  # 信号:RSI 上穿70
            (dataframe['tema'] > dataframe['bb_middleband']) &  # 保护条件
            (dataframe['tema'] < dataframe['tema'].shift(1)) &  # 保护条件
            (dataframe['volume'] > 0)  # 确保交易量不为0
        ),
        ['exit_long', 'exit_tag']] = (1, '某个退出标签')
    return dataframe

请参考策略文档了解如何进入和退出空头交易。

custom_sell

custom_sell已重命名为custom_exit。 现在它在每次迭代中都被调用,与当前利润和exit_profit_only设置无关。

class AwesomeStrategy(IStrategy):
    def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float,
                    current_profit: float, **kwargs):
        dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
        last_candle = dataframe.iloc[-1].squeeze()
        # ...
class AwesomeStrategy(IStrategy):
    def custom_exit(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float,
                    current_profit: float, **kwargs):
        dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
        last_candle = dataframe.iloc[-1].squeeze()
        # ...

custom_entry_timeout

check_buy_timeout()已重命名为check_entry_timeout()check_sell_timeout()已重命名为check_exit_timeout()

class AwesomeStrategy(IStrategy):
    def check_buy_timeout(self, pair: str, trade: 'Trade', order: dict, 
                            current_time: datetime, **kwargs) -> bool:
        return False

    def check_sell_timeout(self, pair: str, trade: 'Trade', order: dict, 
                            current_time: datetime, **kwargs) -> bool:
        return False 
class AwesomeStrategy(IStrategy):
    def check_entry_timeout(self, pair: str, trade: 'Trade', order: 'Order',
                            current_time: datetime, **kwargs) -> bool:
        return False

    def check_exit_timeout(self, pair: str, trade: 'Trade', order: 'Order',
                           current_time: datetime, **kwargs) -> bool:
        return False 

custom_stake_amount

新的字符串参数 side - 可以是 "long""short"

class AwesomeStrategy(IStrategy):
    def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float,
                            proposed_stake: float, min_stake: Optional[float], max_stake: float,
                            entry_tag: Optional[str], **kwargs) -> float:
        # ... 
        return proposed_stake
class AwesomeStrategy(IStrategy):
    def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float,
                            proposed_stake: float, min_stake: Optional[float], max_stake: float,
                            entry_tag: Optional[str], side: str, **kwargs) -> float:
        # ... 
        return proposed_stake

confirm_trade_entry

新的字符串参数 side - 可以是 "long""short"

class AwesomeStrategy(IStrategy):
    def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float,
                            time_in_force: str, current_time: datetime, entry_tag: Optional[str], 
                            **kwargs) -> bool:
        return True

之后:

class AwesomeStrategy(IStrategy):
    def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float,
                            time_in_force: str, current_time: datetime, entry_tag: Optional[str], 
                            side: str, **kwargs) -> bool:
      return True

confirm_trade_exit

已将参数 sell_reason 更改为 exit_reason。 为了兼容性,sell_reason 将在有限的时间内继续提供。

class AwesomeStrategy(IStrategy):
    def confirm_trade_exit(self, pair: str, trade: Trade, order_type: str, amount: float,
                           rate: float, time_in_force: str, sell_reason: str,
                           current_time: datetime, **kwargs) -> bool:
    return True

After:

class AwesomeStrategy(IStrategy):
    def confirm_trade_exit(self, pair: str, trade: Trade, order_type: str, amount: float,
                           rate: float, time_in_force: str, exit_reason: str,
                           current_time: datetime, **kwargs) -> bool:
    return True

custom_entry_price

新增字符串参数 side - 可以是 "long""short"

class AwesomeStrategy(IStrategy):
    def custom_entry_price(self, pair: str, current_time: datetime, proposed_rate: float,
                           entry_tag: Optional[str], **kwargs) -> float:
      return proposed_rate

After:

class AwesomeStrategy(IStrategy):
    def custom_entry_price(self, pair: str, trade: Optional[Trade], current_time: datetime, proposed_rate: float,
                           entry_tag: Optional[str], side: str, **kwargs) -> float:
        return proposed_rate

调整交易位置变化

虽然adjust-trade-position本身没有变化,但是您现在不应再使用trade.nr_of_successful_buys,而是使用trade.nr_of_successful_entries,它将包括空头交易。

辅助方法

stoploss_from_openstoploss_from_absolute中添加了参数is_short。它应该被赋予trade.is_short的值。

    def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime,
                        current_rate: float, current_profit: float, **kwargs) -> float:
        # 一旦利润上涨超过10%,将止损设置为开仓价的上方7%
        if current_profit > 0.10:
            return stoploss_from_open(0.07, current_profit)

        return stoploss_from_absolute(current_rate - (candle['atr'] * 2), current_rate)

        return 1

之后:

    def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime,
                        current_rate: float, current_profit: float, after_fill: bool, 
                        **kwargs) -> Optional[float]:
        # 一旦利润超过10%,将止损保持在开盘价上涨7%的水平
        if current_profit > 0.10:
            return stoploss_from_open(0.07, current_profit, is_short=trade.is_short)

        return stoploss_from_absolute(current_rate - (candle['atr'] * 2), current_rate, is_short=trade.is_short, leverage=trade.leverage)

策略/配置设置

order_time_in_force

order_time_in_force的属性从“buy”更改为“entry”,将“sell”更改为“exit”。

    order_time_in_force: Dict = {
        "buy": "gtc",
        "sell": "gtc",
    }

After:

    order_time_in_force: Dict = {
        "entry": "GTC",
        "exit": "GTC",
    }

order_types

order_types 将所有词从 buy 改成 entry - sell 改成 exit。 并将两个词用 _ 连接。

    order_types = {
        "buy": "limit",
        "sell": "limit",
        "emergencysell": "market",
        "forcesell": "market",
        "forcebuy": "market",
        "stoploss": "market",
        "stoploss_on_exchange": false,
        "stoploss_on_exchange_interval": 60
    }

之后:

    order_types = {
        "entry": "limit",
        "exit": "limit",
        "emergency_exit": "market",
        "force_exit": "market",
        "force_entry": "market",
        "stoploss": "market",
        "stoploss_on_exchange": false,
        "stoploss_on_exchange_interval": 60
    }

策略级别设置

  • use_sell_signal -> use_exit_signal
  • sell_profit_only -> exit_profit_only
  • sell_profit_offset -> exit_profit_offset
  • ignore_roi_if_buy_signal -> ignore_roi_if_entry_signal
    # 这些值可以在配置中被重新定义。
    use_sell_signal = True
    sell_profit_only = True
    sell_profit_offset: 0.01
    ignore_roi_if_buy_signal = False

之后:

    # 这些值可以在配置中被重新定义。
    use_exit_signal = True
    exit_profit_only = True
    exit_profit_offset: 0.01
    ignore_roi_if_entry_signal = False

unfilledtimeout

unfilledtimeout将所有词汇从buy变为entry,从sell变为exit

unfilledtimeout = {
        "entry": 10,
        "exit": 10,
        "exit_timeout_count": 0,
        "unit": "minutes"
    }

修改后为:

unfilledtimeout = {
        "entry": 10,
        "exit": 10,
        "exit_timeout_count": 0,
        "unit": "minutes"
    }

订单定价

订单定价有两处更改。bid_strategy更名为entry_pricingask_strategy更名为exit_pricingask_last_balance属性更名为price_last_balancebid_last_balance属性也更名为price_last_balance。 此外,价格方向现在可以定义为askbidsameother。 更多信息请参考定价文档

{
    "bid_strategy": {
        "price_side": "bid",
        "use_order_book": true,
        "order_book_top": 1,
        "ask_last_balance": 0.0,
        "check_depth_of_market": {
            "enabled": false,
            "bids_to_ask_delta": 1
        }
    },
    "ask_strategy":{
        "price_side": "ask",
        "use_order_book": true,
        "order_book_top": 1,
        "bid_last_balance": 0.0
        "ignore_buying_expired_candle_after": 120
    }
}

修改后为:

{
    "entry_pricing": {
        "price_side": "same",
        "use_order_book": true,
        "order_book_top": 1,
        "price_last_balance": 0.0,
        "check_depth_of_market": {
            "enabled": false,
            "bids_to_ask_delta": 1
        }
    },
    "exit_pricing":{
        "price_side": "same",
        "use_order_book": true,
        "order_book_top": 1,
        "price_last_balance": 0.0
    },
    "ignore_buying_expired_candle_after": 120
}

FreqAI策略

populate_any_indicators()方法已经拆分为feature_engineering_expand_all()feature_engineering_expand_basic()feature_engineering_standard()set_freqai_targets()

对于每个新函数,将自动将对应的(必要时包括时间范围)添加到列中。 因此,使用新逻辑定义特征变得更加简单。

有关每个方法的详细说明,请参阅相应的freqAI文档页面

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
def populate_any_indicators(
        self, pair, df, tf, informative=None, set_generalized_indicators=False
    ):

        if informative is None:
            informative = self.dp.get_pair_dataframe(pair, tf)

        # 第一个循环会自动为不同时间段复制指标
        for t in self.freqai_info["feature_parameters"]["indicator_periods_candles"]:

            t = int(t)
            informative[f"%-{pair}rsi-period_{t}"] = ta.RSI(informative, timeperiod=t)
            informative[f"%-{pair}mfi-period_{t}"] = ta.MFI(informative, timeperiod=t)
            informative[f"%-{pair}adx-period_{t}"] = ta.ADX(informative, timeperiod=t)
            informative[f"%-{pair}sma-period_{t}"] = ta.SMA(informative, timeperiod=t)
            informative[f"%-{pair}ema-period_{t}"] = ta.EMA(informative, timeperiod=t)

            bollinger = qtpylib.bollinger_bands(
                qtpylib.typical_price(informative), window=t, stds=2.2
            )
            informative[f"{pair}bb_lowerband-period_{t}"] = bollinger["lower"]
            informative[f"{pair}bb_middleband-period_{t}"] = bollinger["mid"]
            informative[f"{pair}bb_upperband-period_{t}"] = bollinger["upper"]

            informative[f"%-{pair}bb_width-period_{t}"] = (
                informative[f"{pair}bb_upperband-period_{t}"]
                - informative[f"{pair}bb_lowerband-period_{t}"]
            ) / informative[f"{pair}bb_middleband-period_{t}"]
            informative[f"%-{pair}close-bb_lower-period_{t}"] = (
                informative["close"] / informative[f"{pair}bb_lowerband-period_{t}"]
            )

            informative[f"%-{pair}roc-period_{t}"] = ta.ROC(informative, timeperiod=t)

            informative[f"%-{pair}relative_volume-period_{t}"] = (
                informative["volume"] / informative["volume"].rolling(t).mean()
            ) # (1)

        informative[f"%-{pair}pct-change"] = informative["close"].pct_change()
        informative[f"%-{pair}raw_volume"] = informative["volume"]
        informative[f"%-{pair}raw_price"] = informative["close"]
        # (2)

        indicators = [col for col in informative if col.startswith("%")]
        # This loop duplicates and shifts all indicators to add a sense of recency to data
        for n in range(self.freqai_info["feature_parameters"]["include_shifted_candles"] + 1):
            if n == 0:
                continue
            informative_shift = informative[indicators].shift(n)
            informative_shift = informative_shift.add_suffix("_shift-" + str(n))
            informative = pd.concat((informative, informative_shift), axis=1)

        df = merge_informative_pair(df, informative, self.config["timeframe"], tf, ffill=True)
        skip_columns = [
            (s + "_" + tf) for s in ["date", "open", "high", "low", "close", "volume"]
        ]
        df = df.drop(columns=skip_columns)

        # 在这里添加通用指标(因为在实时情况下,训练时会调用该函数来填充指标)。注意我们确保不要多次添加它们
        if set_generalized_indicators:
            df["%-day_of_week"] = (df["date"].dt.dayofweek + 1) / 7
            df["%-hour_of_day"] = (df["date"].dt.hour + 1) / 25
            # (3)

            # 用户通过在目标前加上&-(请参阅下面的约定)来添加目标
            df["&-s_close"] = (
                df["close"]
                .shift(-self.freqai_info["feature_parameters"]["label_period_candles"])
                .rolling(self.freqai_info["feature_parameters"]["label_period_candles"])
                .mean()
                / df["close"]
                - 1
            )  # (4)

        return df
  1. 特征 - 移到 feature_engineering_expand_all
  2. 基本特征,不扩展到 indicator_periods_candles - 移到feature_engineering_expand_basic().
  3. 不应扩展的标准特征 - 移到 feature_engineering_standard().
  4. 目标 - 将此部分移动到 set_freqai_targets().

freqai - 扩展所有的特征工程

特性将会自动展开。因此,展开循环以及{pair} / {timeframe}部分需要被移除。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
    def feature_engineering_expand_all(self, dataframe, period, **kwargs) -> DataFrame::
        """
        *仅限启用FreqAI策略*
        此函数将自动展开配置定义的特征,这些特征包括`indicator_periods_candles`, `include_timeframes`, 
        `include_shifted_candles`, 和`include_corr_pairs`。换句话说,此函数中定义的单个特征将自动展开为
        `indicator_periods_candles` * `include_timeframes` * `include_shifted_candles` *
        `include_corr_pairs`个特征,都会被添加到模型中。

        所有特征必须以`%`开头,以便被FreqAI内部识别。

        有关这些配置定义参数如何加速特征工程的更多细节请参阅文档:

        https://www.freqtrade.io/zh_CN/latest/freqai-parameter-table/#feature-parameters

        https://www.freqtrade.io/zh_CN/latest/freqai-feature-engineering/#defining-the-features

        :param df: 策略的数据帧,将会接收特征
        :param period: 指标的周期 - 示例用法:
        dataframe["%-ema-period"] = ta.EMA(dataframe, timeperiod=period)
        """

        dataframe["%-rsi-period"] = ta.RSI(dataframe, timeperiod=period)
        dataframe["%-mfi-period"] = ta.MFI(dataframe, timeperiod=period)
        dataframe["%-adx-period"] = ta.ADX(dataframe, timeperiod=period)
        dataframe["%-sma-period"] = ta.SMA(dataframe, timeperiod=period)
        dataframe["%-ema-period"] = ta.EMA(dataframe, timeperiod=period)

        bollinger = qtpylib.bollinger_bands(
            qtpylib.typical_price(dataframe), window=period, stds=2.2
        )
        dataframe["bb_lowerband-period"] = bollinger["lower"]
        dataframe["bb_middleband-period"] = bollinger["mid"]
        dataframe["bb_upperband-period"] = bollinger["upper"]

        dataframe["%-bb_width-period"] = (
            dataframe["bb_upperband-period"]
            - dataframe["bb_lowerband-period"]
        ) / dataframe["bb_middleband-period"]
        dataframe["%-close-bb_lower-period"] = (
            dataframe["close"] / dataframe["bb_lowerband-period"]
        )

        dataframe["%-roc-period"] = ta.ROC(dataframe, timeperiod=period)

        dataframe["%-relative_volume-period"] = (
            dataframe["volume"] / dataframe["volume"].rolling(period).mean()
        )

        return dataframe

Freqai - feature engineering basic

Basic features. Make sure to remove the {pair} part from your features.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
def feature_engineering_expand_basic(self, dataframe: DataFrame, **kwargs) -> DataFrame:
    """
    *只在启用 FreqAI 策略时有效*
    这个函数将自动扩展在配置中定义的特征,
    `include_timeframes`、`include_shifted_candles` 和 `include_corr_pairs`。
    换句话说,在此函数中定义的单个特征将自动扩展为
    `include_timeframes` * `include_shifted_candles` * `include_corr_pairs`
    个特征添加到模型中。

    这里定义的特征不会自动复制到用户定义的
    `indicator_periods_candles`

    所有的特征都必须以 `%` 开头,以便被 FreqAI 内部识别。

    关于这些配置定义参数如何加速特征工程的详细信息,请参见文档:

        https://www.freqtrade.io/en/latest/freqai-parameter-table/#feature-parameters

        https://www.freqtrade.io/en/latest/freqai-feature-engineering/#defining-the-features

        :param df: 接收特征的策略数据帧
        dataframe["%-pct-change"] = dataframe["close"].pct_change()
        dataframe["%-ema-200"] = ta.EMA(dataframe, timeperiod=200)
        """
        dataframe["%-pct-change"] = dataframe["close"].pct_change()
        dataframe["%-raw_volume"] = dataframe["volume"]
        dataframe["%-raw_price"] = dataframe["close"]
        return dataframe

FreqAI - 特征工程标准

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
    def feature_engineering_standard(self, dataframe: DataFrame, **kwargs) -> DataFrame:
        """
        *仅适用于启用了 FreqAI 的策略*
        此可选函数将使用基础时间框架的数据帧调用一次。
        这是最后一个被调用的函数,这意味着进入此函数的数据帧将包含所有其他
        freqai_feature_engineering_* 函数创建的特征和列。

        此函数是进行自定义异国情调特征提取(例如 tsfresh)的好地方。
        此函数还适合于任何不应该在自动扩展中的特征(例如周几)。

        所有的特征名必须以 `%` 开头,以便 FreqAI 内部识别。

        更多有关特征工程的详细信息:

        https://www.freqtrade.io/zh/latest/freqai-feature-engineering

        :param df: 接收特征的策略数据帧
        用法示例: dataframe["%-day_of_week"] = (dataframe["date"].dt.dayofweek + 1) / 7
        """
        dataframe["%-day_of_week"] = dataframe["date"].dt.dayofweek
        dataframe["%-hour_of_day"] = dataframe["date"].dt.hour
        return dataframe

FreqAI - 设置目标

现在,目标将拥有自己的专用方法。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
    def set_freqai_targets(self, dataframe: DataFrame, **kwargs) -> DataFrame:
        """
        *仅在启用 FreqAI 策略时有效*
        设置模型的目标函数所需的必要函数。
        所有目标必须以 `&` 开头,以便被 FreqAI 内部识别。

        更多关于特征工程的详细信息可参考:

        [https://www.freqtrade.io/en/latest/freqai-feature-engineering](https://www.freqtrade.io/zh/latest/freqai-feature-engineering)

        :param df: 将接收目标的策略数据框
        用法示例: dataframe["&-target"] = dataframe["close"].shift(-1) / dataframe["close"]
        """
        dataframe["&-s_close"] = (
            dataframe["close"]
            .shift(-self.freqai_info["feature_parameters"]["label_period_candles"])
            .rolling(self.freqai_info["feature_parameters"]["label_period_candles"])
            .mean()
            / dataframe["close"]
            - 1
            )

        return dataframe

FreqAI - 新数据流程

如果您已创建自己的自定义 IFreqaiModel,并使用自定义的 train()/predict() 函数,并且仍然依赖于 data_cleaning_train/predict(),那么您将需要迁移到新的数据流程。如果您的模型不依赖于 data_cleaning_train/predict(),则无需担心此迁移。这意味着此迁移指南适用于非常小百分比的高级用户。如果您错误地遇到此指南,请随时在 Freqtrade Discord 服务器上询问您的问题。

转换过程首先涉及删除 data_cleaning_train/predict(),并将它们替换为 define_data_pipeline()define_label_pipeline() 函数到您的 IFreqaiModel 类中:```python linenums="1" hl_lines="11-14 47-49 55-57" class MyCoolFreqaiModel(BaseRegressionModel): """ 你在 Freqtrade 版本 2023.6 之前创建的一些酷炫的自定义 IFreqaiModel """ def train( self, unfiltered_df: DataFrame, pair: str, dk: FreqaiDataKitchen, **kwargs ) -> Any:

    # ... 你自定义的内容

    # 删除这些行
    # data_dictionary = dk.make_train_test_datasets(features_filtered, labels_filtered)
    # self.data_cleaning_train(dk)
    # data_dictionary = dk.normalize_data(data_dictionary)
    # (1)

    # 添加这些行。现在我们自己控制管道的拟合/转换
    dd = dk.make_train_test_datasets(features_filtered, labels_filtered)
    dk.feature_pipeline = self.define_data_pipeline(threads=dk.thread_count)
    dk.label_pipeline = self.define_label_pipeline(threads=dk.thread_count)

    (dd["train_features"],
     dd["train_labels"],
     dd["train_weights"]) = dk.feature_pipeline.fit_transform(dd["train_features"],
                                                              dd["train_labels"],
                                                              dd["train_weights"])

    (dd["test_features"],
     dd["test_labels"],
     dd["test_weights"]) = dk.feature_pipeline.transform(dd["test_features"],
                                                         dd["test_labels"],
                                                         dd["test_weights"])

    dd["train_labels"], _, _ = dk.label_pipeline.fit_transform(dd["train_labels"])
    dd["test_labels"], _, _ = dk.label_pipeline.transform(dd["test_labels"])

    # ... 你自定义的代码

    return model

def predict(
    self, unfiltered_df: DataFrame, dk: FreqaiDataKitchen, **kwargs
) -> Tuple[DataFrame, npt.NDArray[np.int_]]:

    # ...你的自己的代码

    # 删除这些行:
    # self.data_cleaning_predict(dk)
    # (2)

    # 添加这些行:
    dk.data_dictionary["prediction_features"], outliers, _ = dk.feature_pipeline.transform(
        dk.data_dictionary["prediction_features"], outlier_check=True)

    # 删除这行
    # pred_df = dk.denormalize_labels_from_metadata(pred_df)
    # (3)

    # 替换为以下的代码块
    pred_df, _, _ = dk.label_pipeline.inverse_transform(pred_df)
    if self.freqai_info.get("DI_threshold", 0) > 0:
        dk.DI_values = dk.feature_pipeline["di"].di_values
    else:
        dk.DI_values = np.zeros(outliers.shape[0])
    dk.do_predict = outliers

    # ...你的自己的代码
    return (pred_df, dk.do_predict)

```

  1. 数据标准化和清洗现在与新的管道定义相一致。这是在新的define_data_pipeline()define_label_pipeline()函数中创建的。不再使用data_cleaning_train()data_cleaning_predict()函数。如果你愿意,你可以覆盖define_data_pipeline()来创建你自己的自定义管道。
  2. 数据标准化和清洗现在与新的管道定义相一致。这是在新的define_data_pipeline()define_label_pipeline()函数中创建的。不再使用data_cleaning_train()data_cleaning_predict()函数。如果你愿意,你可以覆盖define_data_pipeline()来创建你自己的自定义管道。
  3. 使用新的管道进行数据反标准化。使用以下代码块替换这段代码。