Skip to content

特征工程

定义特征

低级特征工程是在用户策略中通过一组名为feature_engineering_*的函数执行的。这些函数设置基础特征,如RSIMFIEMASMA、时间、成交量等。基础特征可以是自定义指标,也可以从任何可以找到的技术分析库中导入。FreqAI配备了一组函数,以简化快速的大规模特征工程:

函数 描述
feature_engineering_expand_all() 此可选函数将根据配置定义的indicator_periods_candlesinclude_timeframesinclude_shifted_candlesinclude_corr_pairs自动扩展已定义的特征。
feature_engineering_expand_basic() 此可选函数将根据配置定义的include_timeframesinclude_shifted_candlesinclude_corr_pairs自动扩展已定义的特征。注意: 此函数不会跨越indicator_periods_candles扩展。
feature_engineering_standard() 此可选函数将一次性使用基础时间框架的数据帧进行调用。这是最后一个要调用的函数,这意味着进入此函数的数据帧将包含所有来自其他feature_engineering_expand函数创建的基础资产的特征和列。此函数是进行自定义奇异特征提取的好地方(例如,tsfresh)。此函数也是任何不需要进行自动扩展的特征的好地方(例如,星期几)。
set_freqai_targets() 设置模型的目标的必需函数。所有目标必须以&开头,以被FreqAI内部识别。

与此同时,高级特征工程在FreqAI配置中的"feature_parameters":{}中处理。在此文件中,可以决定在base_features之上进行大规模特征扩展,例如"包括相关的货币对"、"包括信息化时间框架",甚至"包括最近的蜡烛图"。

建议从提供的示例策略模板(templates/FreqaiExampleStrategy.py)的feature_engineering_*函数开始,以确保特征定义遵循正确的约定。以下是如何在策略中设置指标和标签的示例:

    def feature_engineering_expand_all(self, dataframe: DataFrame, period, metadata, **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内部识别。

        使用以下方法访问元数据,如当前的交易对、时间框架、周期:

        `metadata["pair"]` `metadata["tf"]`  `metadata["period"]`:param df: 输入的Pandas DataFrame,用于接收特征
:param period: 指标的周期 - 示例用法:
:param metadata: 当前交易对的元数据
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["收盘价"] / dataframe["bb_lowerband-period"]
)

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

dataframe["%-相对成交量-period"] = (
    dataframe["成交量"] / dataframe["成交量"].滚动(period).平均()
)

return dataframe

def feature_engineering_expand_basic(self, dataframe: DataFrame, metadata, **kwargs) -> DataFrame:
    """
    *仅在启用FreqAI策略时起作用*
    此函数将自动将config中定义的特征扩展到配置中定义的`include_timeframes`,`include_shifted_candles`和`include_corr_pairs`。
    换句话说,这里定义的单个特征将自动扩展为
    `include_timeframes` * `include_shifted_candles` * `include_corr_pairs`
    数量的特征添加到模型中。

    在此处定义的特征*不会*自动在用户定义的`indicator_periods_candles`上复制

    使用以下方式访问元数据,如当前交易对/时间范围:        `metadata["pair"]` `metadata["tf"]`

        所有特征必须以“%”为前缀,以便FreqAI内部识别。

        :param df: 策略数据框,将接收特征
        :param metadata: 当前交易对的元数据
        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

    def feature_engineering_standard(self, dataframe: DataFrame, metadata, **kwargs) -> DataFrame:
        """
        *仅适用于启用FreqAI的策略*
        此可选函数将在基础时间框的数据框中调用一次。
        这是最后一个要调用的函数,这意味着进入该函数的数据框将包含所有其他 freqai_feature_engineering_* 函数创建的特征和列。

        此函数适用于执行自定义的奇特特征提取(例如tsfresh)。
        此函数适用于任何不应自动扩展的特征(例如星期几)。

        使用以下方法访问元数据,如当前交易对:

        `metadata["pair"]`

        所有特征必须以“%”为前缀,以便FreqAI内部识别。

        :param df: 策略数据框,将接收特征
        :param metadata: 当前交易对的元数据
        使用示例:dataframe["%-day_of_week"] = (dataframe["date"].dt.dayofweek + 1) / 7
        """
        dataframe["%-day_of_week"] = (dataframe["date"].dt.dayofweek + 1) / 7
        dataframe["%-hour_of_day"] = (dataframe["date"].dt.hour + 1) / 25
        return dataframe

    def set_freqai_targets(self, dataframe: DataFrame, metadata, **kwargs) -> DataFrame:
        """
        *仅适用于启用FreqAI的策略*
        此函数需设置模型的目标。
        所有目标必须以“&”为前缀,以便FreqAI内部识别。访问如下所示的当前配对的元数据:

`metadata["pair"]`

:param df: 用于接收目标的策略数据框
:param metadata: 当前配对的元数据
用法示例:dataframe["&-target"] = dataframe["close"].shift(-1) / dataframe["close"]
"""
在这个示例中用户不希望将`bb_lowerband`作为特征传递给模型
因此没有以`%`为前缀然而用户确实希望将`bb_width`传递给
用于训练/预测的模型因此在其前面添加了`%`。

在定义了`基础特征`之后下一步是使用配置文件中强大的`feature_parameters`来扩展这些特征

```json
    "freqai": {
        //...
        "feature_parameters" : {
            "include_timeframes": ["5m","15m","4h"],
            "include_corr_pairlist": [
                "ETH/USD",
                "LINK/USD",
                "BNB/USD"
            ],
            "label_period_candles": 24,
            "include_shifted_candles": 2,
            "indicator_periods_candles": [10, 20]
        },
        //...
    }

上述配置中的include_timeframes是策略中每次对feature_engineering_expand_*()的调用的时间帧(tf)。在这个示例中,用户要求将rsimfirocbb_width5m15m4h时间帧包含在特征集中。

您还可以要求在信息对中包含每个定义的特征的所有特征,使用include_corr_pairlist。这意味着特征集将包括从feature_engineering_expand_*()中的所有include_timeframes提取的所有特征,对于配置中定义的每个相关配对(在这个示例中是ETH/USDLINK/USDBNB/USD)。

include_shifted_candles表示在特征集中包含的先前蜡烛的数量。例如,include_shifted_candles: 2表示FreqAI在特征集中包括过去的2个蜡烛。

总之,这个示例策略的用户创建了如下数量的特征:include_timeframes的长度 * feature_engineering_expand_*()中的特征数量 * include_corr_pairlist的长度 * include_shifted_candles的数量 * indicator_periods_candles的长度 \(= 3 * 3 * 3 * 2 * 2 = 108\)

了解更多有关创造性特征工程的信息

请阅读我们的medium文章,该文章旨在帮助用户学习如何创造性地构建特征。### 通过 metadatafeature_engineering_* 函数进行精细控制

所有的 feature_engineering_* 函数和 set_freqai_targets() 函数都会传入一个 metadata 字典,其中包含了关于 FreqAI 所自动构建特征的 pair, tf (时间周期) 和 period 的信息。因此,用户可以在 feature_engineering_* 函数中使用 metadata 作为锁定/保留特定时间周期、周期和货币对特征的条件。

def feature_engineering_expand_all(self, dataframe: DataFrame, period, metadata, **kwargs) -> DataFrame:
    if metadata["tf"] == "1h":
        dataframe["%-roc-period"] = ta.ROC(dataframe, timeperiod=period)

这将会阻止 ta.ROC() 被添加到除了 "1h" 时间周期外的其他时间周期中。

返回训练中的额外信息

在每次模型训练结束后,重要的指标可以通过在自定义预测模型类中对 dk.data['extra_returns_per_train']['my_new_value'] 赋值 XYZ 来返回到策略中。

FreqAI会将在该字典中赋值的 my_new_value 扩展到返回给策略的数据框中。然后您可以通过 dataframe['my_new_value'] 在策略中使用返回的指标。一个使用返回值的示例是 &*_mean&*_std 值,用于创建动态目标阈值

另一个示例是,用户想要使用交易数据库中的实时指标,示例如下所示:

    "freqai": {
        "extra_returns_per_train": {"total_profit": 4}
    }

您需要设置配置中的标准字典,以便 FreqAI 能够返回正确的数据框形状。这些值可能会被预测模型覆盖,但是在模型尚未设置它们的情况下,或者需要一个默认的初始值时,预设值将被返回。### 为时间重要性赋权重

FreqAI允许您通过指数函数设置weight_factor,使近期数据比过去的数据具有更高的权重:

\[ W_i = \exp(\frac{-i}{\alpha*n}) \]

其中\(W_i\)表示总共\(n\)个数据点中的第\(i\)个数据点的权重。下图显示了不同权重因子对特征集中数据点的影响。

weight-factor

构建数据管道

默认情况下,FreqAI根据用户的配置设置构建一个动态管道。默认设置是强大的,并且设计用于与各种方法配合使用。这两个步骤是MinMaxScaler(-1,1)VarianceThreshold,后者会删除方差为0的任何列。用户可以通过更多的配置参数来激活其他步骤。例如,如果用户在freqai配置中添加了use_SVM_to_remove_outliers: true,则FreqAI将自动将SVMOutlierExtractor添加到管道中。同样地,用户可以在freqai配置中添加principal_component_analysis: true来激活PCA。通过DI_threshold: 1来激活DissimilarityIndex。最后,可以通过noise_standard_deviation: 0.1向数据中添加噪声。最后,用户可以通过use_DBSCAN_to_remove_outliers: true来添加DBSCAN异常值移除。

更多信息

有关这些参数的更多信息,请查看参数表

自定义管道

鼓励用户根据自己的需求定制数据管道,通过构建自己的数据管道来实现。这可以通过在用户的IFreqaiModeltrain()函数中将dk.feature_pipeline设置为他们所需的Pipeline对象来完成,或者如果他们更喜欢不更改train()函数,可以在他们的IFreqaiModel中重写define_data_pipeline/define_label_pipeline函数。!!! note "更多信息可用"     FreqAI使用DataSieve pipeline, 该pipeline遵循SKlearn pipeline API,但添加了其他功能,如X,y和sample_weight向量点删除的一致性, 特征删除和特征名称跟随。

from datasieve.transforms import SKLearnWrapper, DissimilarityIndex
from datasieve.pipeline import Pipeline
from sklearn.preprocessing import QuantileTransformer, StandardScaler
from freqai.base_models import BaseRegressionModel

class MyFreqaiModel(BaseRegressionModel):
    """
     一些很酷的自定义模型
    """
    def fit(self, data_dictionary: Dict, dk: FreqaiDataKitchen, **kwargs) -> Any:
        """
        自定义的fit函数
        """
        model = cool_model.fit()
        return model

    def define_data_pipeline(self) -> Pipeline:
        """
        用户在这里定义他们的自定义特征pipeline(如果他们愿意的话)
        """
        feature_pipeline = Pipeline([
            ('qt', SKLearnWrapper(QuantileTransformer(output_distribution='normal'))),
            ('di', ds.DissimilarityIndex(di_threshold=1))
        ])

        return feature_pipeline

    def define_label_pipeline(self) -> Pipeline:
        """
        用户在这里定义他们的自定义标签pipeline(如果他们愿意的话)
        """
        label_pipeline = Pipeline([
            ('qt', SKLearnWrapper(StandardScaler())),
        ])

        return label_pipeline

在这里,您正在定义用于训练和预测期间要使用的特征集的确切pipeline。您可以通过将它们包装在SKLearnWrapper类中使用大多数 SKLearn转换步骤,如上所示。此外,您还可以使用DataSieve中提供的任何转换。

您可以通过创建继承自datasieve BaseTransform类并实现fit()transform()inverse_transform()方法的类轻松添加自己的转换:

from datasieve.transforms.base_transform import BaseTransform
#导入您需要的任何其他模块

class MyCoolTransform(BaseTransform):
    def __init__(self, **kwargs):
        self.param1 = kwargs.get('param1', 1)```python
    def fit(self, X, y=None, sample_weight=None, feature_list=None, **kwargs):
        # 对 X、y、sample_weight 或 feature_list 进行操作
        return X, y, sample_weight, feature_list

    def transform(self, X, y=None, sample_weight=None,
                  feature_list=None, outlier_check=False, **kwargs):
        # 对 X、y、sample_weight 或 feature_list 进行操作
        return X, y, sample_weight, feature_list

    def inverse_transform(self, X, y=None, sample_weight=None, feature_list=None, **kwargs):
        # 对 X、y、sample_weight 或 feature_list 进行操作/不进行操作
        return X, y, sample_weight, feature_list

提示

你可以在与 IFreqaiModel 相同的文件中定义自定义类。

将自定义的 IFreqaiModel 迁移至新的 Pipeline

如果你已经创建了自己的自定义 IFreqaiModel,并且具有自定义的 train()/predict() 函数,并且仍然依赖于 data_cleaning_train/predict(),那么你需要迁移至新的 pipeline。如果你的模型 依赖于 data_cleaning_train/predict(),则无需担心该迁移。

更多有关迁移的详细信息,请参阅这里

异常值检测

股票和加密市场存在大量的非模式化噪音,以异常数据点的形式出现。FreqAI 实现了多种方法来识别这些异常值,从而减轻风险。

使用差异指数(DI)识别异常值相似性指数(DI)旨在量化模型进行的每个预测所带来的不确定性。

您可以通过在配置中包含以下语句来告诉FreqAI使用DI从训练/测试数据集中删除异常数据点:

    "freqai": {
        "feature_parameters" : {
            "DI_threshold": 1
        }
    }

这将向您的 feature_pipeline 添加 DissimilarityIndex 步骤,并将阈值设置为1。DI允许将在模型特征空间中不存在的预测(异常值)由于低水平的确定性而排除。为了做到这一点,FreqAI测量每个训练数据点(特征向量)\(X_{a}\) 与所有其他训练数据点之间的距离:

\[ d_{ab} = \sqrt{\sum_{j=1}^p(X_{a,j}-X_{b,j})^2} \]

其中 \(d_{ab}\) 是标准化点 \(a\)\(b\) 之间的距离,而 \(p\) 是特征的数量,即向量 \(X\) 的长度。一组训练数据点的特征距离 \(\overline{d}\) 简单地是平均距离的平均值:

\[ \overline{d} = \sum_{a=1}^n(\sum_{b=1}^n(d_{ab}/n)/n) \]

\(\overline{d}\) 量化了训练数据的分布情况,用于与新的预测特征向量 \(X_k\) 和所有训练数据之间的距离进行比较:

\[ d_k = \arg \min d_{k,i} \]

这使得我们可以估计出不相似性指数:$$ DI_k = d_k/\overline{d} $$

你可以通过调整 DI_threshold 来增加或减少训练模型的外推。较高的 DI_threshold 意味着 DI 更宽容,允许使用离训练数据更远的预测结果,而较低的 DI_threshold 则产生相反的效果,因此会丢弃更多的预测结果。

下面是描述 3D 数据集 DI 的图示。

DI

使用支持向量机(SVM)识别异常值

你可以告诉 FreqAI 使用支持向量机(SVM)从训练/测试数据集中删除异常数据点,方法是在配置中加入以下语句:

    "freqai": {
        "feature_parameters" : {
            "use_SVM_to_remove_outliers": true
        }
    }

这将在你的 feature_pipeline 中添加 SVMOutlierExtractor 步骤。SVM 将在训练数据上进行训练,并删除任何被 SVM 判定为超出特征空间的数据点。

你可以选择在配置的 feature_parameters.svm_params 字典中提供 SVM 的其他参数,例如 shufflenu

参数 shuffle 的默认设置为 False,以确保结果一致。如果设置为 True,在同一数据集上多次运行 SVM 可能会产生不同的结果,因为 max_iter 对于满足要求的 tol 来说太低了。增加 max_iter 可以解决这个问题,但会导致过程花费更长的时间。参数nu表示应考虑的异常值数量,应在0和1之间。

使用DBSCAN识别异常值

您可以配置FreqAI使用DBSCAN对训练/测试数据集或预测中的异常值进行聚类和移除,只需在配置中激活use_DBSCAN_to_remove_outliers

    "freqai": {
        "feature_parameters" : {
            "use_DBSCAN_to_remove_outliers": true
        }
    }

这将在feature_pipeline中添加DataSieveDBSCAN步骤。DBSCAN是一种无监督机器学习算法,可以对数据进行聚类,而无需知道应该有多少个聚类。

给定数据点的数量\(N\)和距离\(\varepsilon\),DBSCAN通过将所有在距离\(\varepsilon\)内有\(N-1\)个其他数据点的数据点设置为核心点来对数据集进行聚类。距离一个核心点\(\varepsilon\)以内,但是在距离自身\(\varepsilon\)内没有\(N-1\)个其他数据点的数据点被称为边缘点。一个聚类是核心点边缘点的集合。在距离\(<\varepsilon\)没有其他数据点的数据点被视为异常值。下图显示了一个具有\(N=3\)的聚类。

dbscan

FreqAI使用sklearn.cluster.DBSCAN(详细信息可在scikit-learn的网页这里(外部网站)找到),其中min_samples\(N\))被取为特征集中时间点(蜡烛图)数量的¼。eps\(\varepsilon\))会自动计算为从特征集中所有数据点的成对距离计算的k距离图中的拐点位置。