特征工程¶
定义特征¶
低级特征工程是在用户策略中通过一组名为feature_engineering_*
的函数执行的。这些函数设置基础特征
,如RSI
、MFI
、EMA
、SMA
、时间、成交量等。基础特征
可以是自定义指标,也可以从任何可以找到的技术分析库中导入。FreqAI配备了一组函数,以简化快速的大规模特征工程:
函数 | 描述 |
---|---|
feature_engineering_expand_all() |
此可选函数将根据配置定义的indicator_periods_candles 、include_timeframes 、include_shifted_candles 和include_corr_pairs 自动扩展已定义的特征。 |
feature_engineering_expand_basic() |
此可选函数将根据配置定义的include_timeframes 、include_shifted_candles 和include_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
)。在这个示例中,用户要求将rsi
、mfi
、roc
和bb_width
的5m
、15m
和4h
时间帧包含在特征集中。
您还可以要求在信息对中包含每个定义的特征的所有特征,使用include_corr_pairlist
。这意味着特征集将包括从feature_engineering_expand_*()
中的所有include_timeframes
提取的所有特征,对于配置中定义的每个相关配对(在这个示例中是ETH/USD
、LINK/USD
和BNB/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文章,该文章旨在帮助用户学习如何创造性地构建特征。### 通过 metadata
对 feature_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\)表示总共\(n\)个数据点中的第\(i\)个数据点的权重。下图显示了不同权重因子对特征集中数据点的影响。
构建数据管道¶
默认情况下,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异常值移除。
更多信息
有关这些参数的更多信息,请查看参数表。
自定义管道¶
鼓励用户根据自己的需求定制数据管道,通过构建自己的数据管道来实现。这可以通过在用户的IFreqaiModel
的train()
函数中将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}\) 是标准化点 \(a\) 和 \(b\) 之间的距离,而 \(p\) 是特征的数量,即向量 \(X\) 的长度。一组训练数据点的特征距离 \(\overline{d}\) 简单地是平均距离的平均值:
\(\overline{d}\) 量化了训练数据的分布情况,用于与新的预测特征向量 \(X_k\) 和所有训练数据之间的距离进行比较:
这使得我们可以估计出不相似性指数:$$ DI_k = d_k/\overline{d} $$
你可以通过调整 DI_threshold
来增加或减少训练模型的外推。较高的 DI_threshold
意味着 DI 更宽容,允许使用离训练数据更远的预测结果,而较低的 DI_threshold
则产生相反的效果,因此会丢弃更多的预测结果。
下面是描述 3D 数据集 DI 的图示。
使用支持向量机(SVM)识别异常值¶
你可以告诉 FreqAI 使用支持向量机(SVM)从训练/测试数据集中删除异常数据点,方法是在配置中加入以下语句:
"freqai": {
"feature_parameters" : {
"use_SVM_to_remove_outliers": true
}
}
这将在你的 feature_pipeline
中添加 SVMOutlierExtractor
步骤。SVM 将在训练数据上进行训练,并删除任何被 SVM 判定为超出特征空间的数据点。
你可以选择在配置的 feature_parameters.svm_params
字典中提供 SVM 的其他参数,例如 shuffle
和 nu
。
参数 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\)的聚类。
FreqAI使用sklearn.cluster.DBSCAN
(详细信息可在scikit-learn的网页这里(外部网站)找到),其中min_samples
(\(N\))被取为特征集中时间点(蜡烛图)数量的¼。eps
(\(\varepsilon\))会自动计算为从特征集中所有数据点的成对距离计算的k距离图中的拐点位置。